Testing Tests My Patience

Jamesfeltonthomas
7 min readMar 17, 2024

--

There are so many wonderful aspects of software development that I deliberately chose to improve to avoid test writing. Now that I am focused on advancing from a junior developer to mid and above I can no longer avoid the deep dive into test creation necessary to improve my skills.

I was unlucky enough that my first developer gig out of coding BootCamp didn’t require us to write tests, so I got a year and some change into my career without experiencing the joy of testing. We learned TDD and test writing in our Bootcamp curriculum so the skill wasn’t foreign, I just hadn’t written tests for enterprise-level software before my current position and that is a different monster entirely from testing your Hello World or ToDoprojects.

But at my current gig testing is a must. Suites must be built for each code structure we implement (component or function), and/or updated with each task we complete.

So to practice I’m writing tests and blogs to help build my skills and understanding of the fundamentals of proper testing.

I have some personal projects that I have built to increase my familiarity with different software packages and practices I have encountered at work so I’m going to use one of them as the example for this trek through the land of component testing.

Why are testing skills so important?

Testing skills are important because they demonstrate a true understanding of how the component works. Your skill in creating components that are reusable, scalable, and tolerant of edge cases will be increased as you broaden your testing abilities. Improved testing skills will also help increase your understanding of the team’s quality assurance developer role, and save you time avoiding tedious extra pull requests in the future.

“If you can test it, you understand it.”

I didn’t read that anywhere, but after building some testing suites that required mocking dependencies I’ve come to believe it so I quoted to increase the effect.

I enjoy approaching things with a set of guidelines or a format to follow, and in my reading on testing, I came across the AAA pattern. I don’t know if it’s the best, but I like it the most because I’ve been a AAA member for over 15 years.

The Method

AAA in this case stands for Arrange, Act, Assert, which are steps in how to pattern your testing suites. I love simple instructions so this pattern is perfect for me and anyone else who wants a systematic approach to testing. It’s also applicable to any testing package so the fact that I’m using Jest won’t be an issue if you fancy Mocha.

Here’s a quick overview of each of the A’s:

  • Arrange — instantiate objects, initialize variables, and mock dependencies if you have any.
  • Act— invoke functions, render components, simulate button presses, etc.
  • Assert — ensure that your assumption or expected result is as desired.

Happy Path First

Now that we have established how to format our suites we need to know what to test. I love to keep things simple so always start with the happy path first. For example, if it is a component suite, did the component properly render? Or if we are testing a function, did we get our expected result from the input?

Yes, I understand that writing out two sentences doesn't explain testing, but properly labeling your tests is another tip so I snuck that in there. For more great testing tips here is a great article recommended by my team tech lead so I’m passing it on to you — https://leanylabs.com/blog/good-unit-tests/#follow-arrange-act-assert

We have the format and starting point, so let's apply them to creating a basic testing suite.

The Code

Let’s assume all the steps needed to implement the desired testing library in your project have already been followed, but for those who haven’t here are Jest’s docs: https://jestjs.io/docs/getting-started#running-from-command-line

If you’re using CRA ( create react app ) or CNA ( create next app ), testing software was shipped as part of your application and you can skip right to the fun part, writing tests.

We will start our example with a simple component.

import { Box, Typography } from "@mui/material";
import threeDoors_front from "../../assets/threeDoors_front.mp4";

const Home = () => {
return (
<Box
sx={{
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
marginTop: "10px",
gap: "20px",
width: "100%",
}}
>
<Typography variant="h4">Welcome To My Building</Typography>
<video autoPlay muted loop id="homePage-video">
<source src={threeDoors_front} type="video/mp4" />
</video>
</Box>
);
};

`Home.js` is a functional component that displays an H4 element with the page title and a looping video. There’s no state to manage, the component only renders text and a video so it’s perfect for our first test.

Where should the test live?

I’ve read some different takes on where your component testing files should be kept, such as the component and testing suite should be kept in the same directory, but I prefer to keep things separated by purpose. I organized the structures of my application into directories grouped by concern: UI components ( layout, pages, child components ), state/middleware, utilities, assets, styles, and tests.

screenshot of project directory tree

I think it is easier to find things when organized in this manner, but where the tests stay is less important when learning than if they work properly so let’s not overthink this part.

AAA Breakdown

Our first testing suite Home.test.js will be basic and cover the happy path first. Our suites are going to be titled using the suiteName.test.js format. The *.test.js suffix is specific to the testing library I’m using, but utilizing a consistent pattern is what I recommend. The suiteNameportion of your title can be anything you like but to save yourself a lot of confusion 6 months from now when looking back, at least match the name with the component or function it is testing.

In our first step Arrange, we begin by importing the code structures (components, testing methods, etc) necessary for our test to run successfully. Next, we organize the suite using the describe and it testing functions. While the describe function is used to group related individual tests defined within the it function/s, it is not mandatory to use both to have a passing suite.

I prefer using the describe function within all my suites because it visually organizes the file into relevant sections allowing me to give each a descriptive title for increased clarity. How well you describe your tests will play a role later in your ability to effectively error handle if/when tests begin to fail.

The final aspect of arranging our suite is to set variables or instantiate any objects needed for testing. We will utilize the renderfunction to mount our component to a virtual DOM inside the testing suite. This allows us to interact with the component like a user in the browser.

//ARRANGE 

import { render, screen } from "@testing-library/react";

import Home from "../../components/pages/Home";


describe("Home page testing suite", () => {

it("Component renders with page title", () => {

render(<Home />);


});

});

In our second step Act, we invoke our testing functions and save the results to variables we created. Here we will employ the screen function, which allows us to query different elements of our mounted component, and later test our hypothesized outcomes in the final step.

//ACT

import { render, screen } from "@testing-library/react";

import Home from "../../components/pages/Home";

describe("Home page testing suite", () => {

it("Component renders with page title", () => {

render(<Home />);

const result = screen.getByRole("heading");

});

});

In our third and final step Assert, we perform comparisons to assess our original assumptions about the component. This is where the magic happens, the rest is all buildup to this point.

The expect function and its methods allow us to create test cases for a myriad of different scenarios, cases, or states. Since we want to keep it simple for our first test of the suite let's just look for the page title.

<Typography variant="h4">Welcome To My Building</Typography>
//ASSERT

import { render, screen } from "@testing-library/react";

import Home from "../../components/pages/Home";


describe("Home page testing suite", () => {

it("Component renders with page title", () => {

render(<Home />);

const result = screen.getByRole("heading");

expect(result).toHaveTextContent(/Welcome To My Building/i);

});

});

Honestly, the final step is the least complicated if you organize the rest of the suite correctly because you are simply affirming whether or not your assertion was true, false, matching, greater than, undefined, etc. For a list of the different queries you can use to find different elements on your component check out: https://testing-library.com/docs/queries/about/

Conclusion:

After utilizing the AAA pattern to build the suites of my application, I confess to feeling more organized in my test writing approach. An immediate example was when writing this blog I learned so much that I updated my example suite to fit the pattern. Who would’ve thought reading, doing, and then writing about it would improve my skills that fast? Anyway, my next topics cover handling the Sad Path and mocking component dependencies in the tests of more complex components; I hope writing blog number two has the same effect. See you then.

Happy Coding Yall!

--

--