Test Driven Development Will Drive You Back to Basics

Wendy Raven McNair
8 min readApr 10, 2022

--

A child is driving a miniature toy car.
Photo by Artyom Malyukov: https://www.pexels.com/photo/child-driving-toy-car-9889710

My coding journey started in 2020 when the pandemic began and my first scripting language was JavaScript. I envisioned basic JS concepts would be an easy given now that I’m 2 years into it. I was mistaken.

I’m teaching myself Test Driven Development (TDD) which is a coding strategy in which the developer first codes a test that will check a specific part of the code to build the application. Then writes that specific code to pass the test. By building these automated tests, the developer can be confident that the code is error free and the app user will have the experience intended.

Alternatively, the developer can code the application without writing tests. And then test the application manually by using it to see if there are any problems that need to be fixed. However the developer is likely to miss issues that could be released to the public and cause major problems. And the larger and more complex the application, the more likely issues will be missed.

TDD helps you write better code but it’s more challenging than just writing the code. You have to choose the correct testing resource(s) and you have to learn new syntax and how to apply the test to your code. Very challenging, particularly for a new developer who’s still learning the ins and outs of basic coding.

But I’m 2 years in and my basic coding skills are solid. So planning to move forward to develop my TDD skills should be pretty easy for me, right?

We plan, God laughs!

I’m using TDD to build a React application so I’m using the Jest library. Initially, I chose a testing utility (Enzyme) to assist in testing my components’ output and I struggled with it for a couple of days before ultimately abandoning it due to adaptor issues.

Then I opted for Jest and the React Testing Library since that dependency comes included when using the Create React App environment to build a React app.

I decided to build a React app that an English language learner can use to create subjects (ex. doctor’s visit) and then add words related to that subject containing definitions and sentences necessary to communicate effectively in that English environment. They can use the app to refresh their English knowledge and practice conversing on different subjects in English.

To get started, I used the create-react-app command in the terminal to create a new application.

I created a SubjectsList.js file to hold the subjects functional component and a SubjectsList.test.js file to hold the tests for that component.

Then I thought of the app functionality I wanted to code. I decided that if the user has not created any subjects, the application should show the message “There are no subjects.” Once a subject is created, the message should disappear.

Since I was practicing TDD, I began by writing the test first in the test file. I needed to render the specific component whose output I wanted to test. So I imported the render and screen methods from the react testing library and I imported my SubjectsList component so I would have access to it.

This allowed me to render the SubjectsList component inside of the test. Either test or it can be used to set up the test, I used it. I passed in a brief detail of what the test would be testing and then placed the SubjectsList render inside the callback function.

Now that the SubjectsList was virtually rendered, I needed to capture the element containing the message “There are no subjects.” The message should exist on the virtually rendered SubjectsList because no subjects had been created. I used getByText to store a copy of the message-containing element in the variable noSubjectsMessage.

Then I used expect to write an assertion that the noSubjectsMessage variable containing the message should be in the rendered document. My completed test looked like this:

Now to run the test. I opened the terminal and cd into my app directory and then ran the command npm test. [ NOTE The test will continue to run and updates with each change and save; to stop the test use the command control + c. ]

A failing test was reveal as expected. It showed a single Test Suites (my test file) had failed. It also showed I had written one test in that file that had failed which was why the whole suite had failed.

I scrolled to see more information on the test fail and located the test by its detail showing in red. It revealed that the test failed due to a TestingLibraryElementError because it was unable to find an element with the text message “There are no subjects.”

I hadn’t written the code in my component file to show that message yet so the test was supposed to fail. So far so good.

Now I could hop into my component file and write the code to show that message and make the test pass. Since I wanted the message to show when there were no subjects then disappear when there were subjects, I decided to use the ternary operator. It’s a simple conditional statement that checks for a condition (ex. do subjects exist) and if it’s true there is one result (ex. no message) and if it’s false there’s another result (ex. There are no subjects.).

I imported useState to set up subjects state inside the component. Since the user would be able to create more than one subject, I needed an array. And since there were no subjects yet, the array would start off empty.

I must admit, I was feeling relieved that the hard part was over and all I had to do was code out my simple ternary statement inside of the return to get the test to pass. My back-to-basics moment occurred while trying to code this basic ternary statement. I used the code below and my test did NOT pass!

It resulted in the exact same fail message as before. The logic appeared sound;

  • check the subjects variable for subjects
  • if it did have subjects, don’t create the message
  • if it did not have subjects, create the no subjects message

The subjects variable clearly did NOT have subjects due to the empty array. This subjects variable was being used to establish truthy or falsy in my ternary statement and it was empty, NO SUBJECTS! So why was the test still failing?

It took me quite a while before I figured out this didn’t work because an array evaluates to truthy, even when it’s empty! So my subjects variable was basically registering, “Yes, subjects exist even though the array is empty and has no subjects in it.” And because of that, the “There are no subjects” message was not creating. Since the test expected that message to exist in the virtually rendered component, it failed because the message was never created.

It took me a lot of time to figure this out because I was confident I knew my JavaScript basics regarding what was truthy and what was falsy. It never crossed my mind that the issue was the empty array. I wondered if the computer was malfunctioning before I considered it could be the empty array. That’s how confident I was of my JavaScript basics.

I eventually went through the code, checking every piece regardless of if I thought it was correct. I was finally alerted to what was going on when I used the bang (!) operator to check the truthy or falsy status of the subjects value. The double bang (!!) reveals a value’s Boolean (true or false) status. So I expected !!subjects (which was the same as !![]) to be false. It wasn’t. It was true.

Imagine my shock, 2 years in, making that mistake!

This TDD road is very challenging but it’s leveling up my programming skills by driving me back to basics. As a developer, that’s exactly what I need.

P.S. If you’re wondering how I fixed the issue…

ORIGINAL: I referenced a falsy empty string (“ ”) instead of the truthy empty array. An empty string definitely is falsy (I checked even though I knew this as fact). So I changed the subjects initial state to an array containing an empty string. Then I used index notation [0] to reference this empty string (“ ”) instead of an empty array in the ternary statement. The code below finally passed the test.

REFACTORED: While thinking over my initial solution, I realized that starting subjects with an empty string contained in an array could lead to subject counting errors or rendering problems when adding or deleting subjects. The permanently empty string contained in the array would cause these issues. Ideally, the array should start containing nothing, not even an empty string. But I need the subjects variable to start by being falsy and an empty array is truthy.

So I removed the empty string from the array in useState but kept everything else the same and the test passed!

However, after further consideration, even though the test passed, using index notation zero did not truly reflect what my code should be doing. Explicitly checking for the 0 index implies I specifically want that index and I don’t. I want to ensure the whole array is completely empty. A better representation for that is to use subjects.length. Using subjects.length satisfies the falsy requirement, because it evaluates to zero when subjects is empty and zero is falsy, and it clearly indicates that I’m checking to see if there are any subjects at all in the array. So I refactored subjects in my ternary statement and replaced subjects[0] with subjects.length.

And my test still passed!

So yes, TDD is challenging but it drives you back to basics and allows you to think very intentionally about your code, resulting in cleaner code with minimal if any errors that would disrupt a user’s experience when interacting with your app.

--

--

No responses yet