We all know writing unit tests is important but sometimes it feels like it can take up more time than the feature work itself. I’ve found a couple of handy hacks that I feel have increased my speed when writing tests whilst also improving their quality and, being the kind fellow I am, I’m going to share those with you:
Hack 1: Use Testing Playground
I guess the first tip in this article is - use Testing Library. I didn’t make this its own point because it is already so popular. But if you are not using it yet, make sure you do!
Unit testing with Testing Library is really easy and intuitive. Despite this, it can still be challenging to find the right queries or to understand why an element isn't being matched.
Enter Testing Playground.
Testing Playground allows you to render a component in a sandbox providing you with direct visual feedback of the component. It also allows you to interact with the rendered component to come up with the best queries to select elements. And, like Testing Library, everything it does is with accessibility (a11y) in front of mind so it teaches you about the importance of a11y and best practices while you use it.
There are many ways you can use Testing Playground including a chrome extension and a browser based app.
The best way I have found which has been an absolute time saver for me though is by invoking screen.logTestingPlaygroundURL()
right from the test block itself. I usually find myself doing this as soon as I get the component rendering, just to get the lay of the land and work out what parts of it my test might like to interact with.
Hack 2: Use test.todo
Please don’t jump down my throat, but I have tried Test Driven Development and didn’t like it. Like anarchism, I think it sounds awesome in theory, but found that it actually slowed down my development cycle when I tried to implement it.
I still like the idea of getting some thinking about testing down before I finish building a feature though and have settled on a process that, for me, seems to work well and keep my development moving along.
I now use Jest’s test.todo
to record what I am going to test as I am planning to and building out a feature (Big thanks to Karl for first introducing me to the idea!).
My usual process goes a bit like this. First I capture the requirements spelled out for me by my awesome Product Owner (Hi Biz!) in test.todo
form, like a todo list. Then, as I am building and encounter other edge cases and important testing areas I add these as test.todo
’s too. That way, when it comes to testing time, I have thought through a lot of what I am going to test and am less likely to miss testing edge cases or important functional requirements.
A simple example for a test.todo
for the following <UserDetails />
component:
import React from "react";
export interface User {
firstName: string;
lastName: string;
username: string;
emailAddress: string;
SEN: string;
}
export interface ShowHideUserDetailsProps {
showDetails: boolean;
user: User;
}
const UserDetails = ({ showDetails, user }: ShowHideUserDetailsProps) => (
<>
{showDetails ? (
<div>
<h1>User Details</h1>
<ul>
<li>{user.firstName}</li>
<li>{user.lastName}</li>
<li>{user.username}</li>
<li>{user.emailAddress}</li>
<li>{user.SEN}</li>
</ul>
</div>
) : (
<div>
<h1>Privacy Protected</h1>
</div>
)}
</>
);
export default UserDetails;
Might be as follows:
describe('<UserDetails />', () => {
test.todo('Should show user details when show details is true');
test.todo('Should NOT show user details when show details is false');
});
Hack 3: Use builder functions
I used to find myself creating objects for each test to mock out values for testing. Then I wrote another component which used the same object and mocked it out again there. There’s got to be a better way, I thought. And Matt Smith at FinoComp introduced me to one.
I now use builder functions which return commonly used object types in testing and which allow properties to be overridden everywhere. There is certainly a little bit of extra time needed to set them up but I find that, once they are done, the next time you have to interact with that object you are so glad they are there. For example, for a given TS interface:
export interface User {
firstName: string;
lastName: string;
username: string;
emailAddress: string;
SEN: string;
}
export interface ShowHideUserDetailsProps {
showDetails: boolean;
user: User;
}
You might have a builder function that looks like this:
export const buildShowHideUserDetailsProps = (overrides?: Partial<ShowHideUserDetailsProps>): ShowHideUserDetailsProps => {
const defaultShowHideUserDetailsProps = {
showDetails: false,
user: {
firstName: "Jazmyne",
lastName: "Jacobs",
username: "Kylee_Skiles37",
emailAddress: "Rashawn13@gmail.com",
SEN: "SEN-123456"
}
};
return { ...defaultShowHideUserDetailsProps, ...overrides};
};
There are some limitations to this pattern, however, as they become less useful with deeply nested object types. Additionally, they do require some upkeep when object types change in the codebase which brings me to my next point...
Hack 4: Use a tool to mock your Typescript types
Look, I’m going to be straight here. This is the part where I plug my own work but at least I left it for last, right?
Whenever I found myself creating another mock object for testing I kept looking at my Typescript types and thinking, can’t something look at that and do it for me? This sent me down a search for a solution and I was so stoked to find intermock which is a command line tool that does just that. While it is still a work in progress and has some limitations I have found it super helpful when writing tests.
I did find using the combination of the CLI and copy/pasting from the terminal a little cumbersome though. How could I make this even easier I thought.
Enter my VSCode extension, Emulative. Simply select your type name, run a command through the Command Palette in VSCode and you can use Emulative to produce Typescript objects, Json objects or the aforementioned builder functions from Typescript types. These are automatically copied to the clipboard but the plain objects can also be sent to a new scratch file.
But wait, there’s more! Where I work at Easy Agile we have a bunch of properties we work with from Jira which aren’t accurately represented by string
or number
. With Emulative you can set up key/value pairs which will overwrite any matching properties which are found in your types.
Shout out to Easy Agile for giving me the time, resources and encouragement during an Inception Week to work on Emulative!
Well, that’s it for me, I hope you find some of these tips and tricks useful for speeding up front end testing.
Either way please feel free to sound off in the comments about nifty tricks you have found to improve your unit testing speed (or just how wrong I am about TDD).
Hi 👋 I'm John, a developer and psychologist at Easy Agile. When I'm not building products customers love to use, you will usually find me in the surf with my wife and daughter, spending too much time agonising over my coffee grind size or walking my golden retriever Norman.
If you want to ask me more about Inception Weeks or chat about anything else agile, software development (or surfing!) reach out on Twitter.
Top comments (0)