Tutorial on Testing React with TDD— Part 2
By Lawton Spelliscy
In part 1 I walked you through the basics of creating tests with React using a TDD approach. In this blog, I will build on our user story, and show you how to implement more complex tests, and introduce more features from our test libraries.
Now let’s handle the error case when a user enters invalid input.
Non-numerical values separated by ‘+’ symbols are entered in the equation field
User clicks the ‘=’ button
Error message should display below equation “Invalid input. Please input only numbers.”
No value should display beside the equation
Test # 1
Similar to last time, let’s start with testing the math utility. If we pass any strings that have non-numeric values, we want to make sure that our utility function will return a value that indicates a fail. In my case, the function will return ‘NaN’ as my value and the error display will be handled by the UI.
Note: We need to use syntax expect(rtnValue).to.be.NaN not expect(rtnValue).to.equal(NaN) this is because in JS NaN !== NaN
Now, here is where running your tests prior to attempting implementation is important. If we run the tests we will see that they are already passing! This is because parseInt(‘one’) will return NaN and 1 + NaN = NaN. Running our tests prior to implementation can help us avoid false positive, but also save us time as we’re not wasting efforts on implementing functionality that already exists. For this case there was an edge case that wasn’t considered. Here we will add another test case:
Test # 2
If we run this test we get:
Here is some unexpected behaviour from parseInt; it converted the integer in our string and ignored the rest.
Implementing # 2
Here we are manually checking whether our value, isFinite, and whether parseInt returns NaN. Now, let’s run the tests again.
While our test is now passing we’ve actually broken a previous test. What went wrong? If you look closely you will see that my logic is backwards, I will return NaN if parseInt doesn’t return a NaN. Consequently, because of this logic, our utility class will always return NaN. This highlights how robust your code can get with tests! As your test suite grows you can help avoid small bugs popping up from logic mistakes. Let’s fix this issue by reversing our if/else.
Test # 3a
Alright, now we need to make sure that our UI is displaying a proper error message.
Here we want to verify that when parseEquation returns NaN there is a label present with the valid error message.
Implementing # 3a
Here if parseEquation returns NaN we update our errorMessage otherwise we update our calculatedResult.
Test # 3b
If we run our tests they will pass. However, what we haven’t considered is what happens if our state already changed. For example, after I received the error message and fixed my equation, I need to make sure that not only do I show the result, but I need to hide the error message as well. Enzyme allows you to directly set the state of a component, allowing us to mock a test setting. Let’s update our previous test and create a new test to verify that we our clearing the result and error message when we don’t want to display them.
Implementation # 3b
So now we just need to make sure we are resetting our state values.
If we run our tests they should now be passing.
That is the end of my testing example. When first approaching TDD, I think a lot of people are hesitant to begin because of the overhead and initial awkwardness of writing tests before knowing the implementation. I hope my example displayed some of the advantages that make you see the value in TDD, and why it’s worth a try. I personally really enjoy the structured approach that comes with TDD, because we can create such small unit tests, we know immediately when we’ve correctly implemented the functionality. There’s also the huge added bonus of having a massive test suite growing organically during development. If you have any questions or feedback, leave me a comment below!
One of TribalScale’s first employees, Lawton Spelliscy, is a Lead Agile Software Engineer at TribalScale. He’s currently helping TribalScale’s Transformation Services through defining and running the Engineering component with some of the biggest brands. A love of all things nerdy has led him down the dark path to programming. An oddball at heart, his interests also include hiking, travelling, musicals, board games, and what he calls ‘dancing’.