Logo

Blog

Sleeping Better At Night

In this talk, Tomasz gives an overview on why you should test your code, the different types of tests and shares some tools that make testing easier. Here’s the talk link 📹

Why are you afraid of pushing to production on Friday?

Your code doesn’t care about which day of the week it is, it doesn’t know the difference between tuesday and friday so why should you?

Here’s a useful diagram that demonstrates the probability you’ll break the production build using your YOLO (You Only Live Once) change:

probability-of-breaking-prod-diagram

So this brings up an important question: can you avoid this fear of breaking production on Friday? Well the answer is yes. By testing your code.

As software developers, we constantly keep asking the same question when writing code :

“Does this code actually work?”

but before answering that, we should ask a more important question:

Why do we care in the first place ?

Writing code with a couple of bugs is actually normal. If you push to production and there’s a bug, it’s not the end of the day, you’ll still get paid. (Of course if you ship really buggy unmaintainable code then yes you’ve got a problem).

But this is about something else entirely, it’s about empathy.

Firstly, there’s empathy for your users. You care about there experience. especially if you’re building an app that helps them make a living (Publishing videos, selling items, selling services, etc.)

Secondly, there’s empathy for other team members. We don’t want to make a YOLO change that might break production and force other team members to work during their time off.

Finally, we care because it makes us feel safe. We don’t want to worry about work all the time when we’re at home.

The Testing Spectrum

This is what the testing spectrum looks like:

testing-spectrum

Testing comes in many shapes and forms:

linting & formatting tools

Linting tools when (i.e. ESLint) combined with formatting tools ( i.e. Prettier) count as testing.

Why? because when you press ctrl+S to save, if your code isn’t formatting, this means you have a syntax error. And you won’t push code that isn’t properly formatted.

ESLint also helps in catching bugs; for example, you may have written a function and it’s not working, only to find out you haven’t called the function in the first place.

forgot-to-call-function-meme

Types

Strongly-typed code means you’re defining the types of variables you’re using in your code. You’ll define the return type of a function, the types of variables you’re passing to the function, etc. Using a strongly typed language (like TypeScript) helps in preventing a lot of bugs.

Having types is great but even 100% typed code has bugs. Think of apps written in a strongly typed language like Java or C++, they have bugs.

1// this code is in TypeScript, which is
2// strict syntactical super-set of JavaScript
3
4// here we're explicitly declaring that the variable
5// called name can only hold string values
6
7let name:string = "Tomasz";
8
9
10// here we're explicitly declaring that the variable
11// called age can only hold numbers
12let age:number = 30;
13
14// add function in JavaScript
15// we can pass two strings to the function and it will work,
16//but that's not the functionality we want
17
18function add = (a, b)=>{
19 return a + b;
20}
21
22// add function in TypeScript
23// we're defining the types of the function parameters
24//and the type of its return value
25
26function add(a: number, b: number): number {
27 return a + b;
28}

Unit Tests

Unit testing is when you test a component on its own and making sure that it works as expected.

Test coverage is defined as a technique which determines whether our test cases are actually covering the application code and how much code is exercised

Why you shouldn’t aim for 100% test coverage.

Having 100% test coverage doesn’t reflect confidence in your software. Unit tests don’t protect you against misunderstanding the requirements.

Okay, if you’re not going to test all of your code, which parts should you test? Which percent of coverage should you aim for?

One popular opinion about writing tests, is to make sure that your tests resemble the way your software is used:

kent-c-dodds-testing-tweet

Here’s an example of tests that don’t resemble the software used.

1it("renders Toggle component", () => {
2 expect(wrapper.find(Toggle)).toHaveLength(1)
3
4 // users don't care about props
5 expect(wrapper.find(Toggle)).toHaveProp("label", "Behold,the best toggle")
6 expect(wrapper.find(Toggle)).toHaveProp("renderLabelBefore", true)
7 expect(wrapper.find(Toggle)).toHaveProp("toggleOnClick", false)
8
9 // testing if a component has a certain class is something that the user doesn't see
10 wrapper.find(".toggle").simulate("click")
11 expect(wrapper.find(Toggle)).toHaveProp("label", "Behold,the best toggle")
12})

If you change the implementation of a component, for example you changed it from a React component to a Vue component (Same behavior just different code), your tests should still pass. Your tests should not be aware of the implementation beneath what your users experience.

A great example of a library that focuses on testing what the user sees and interacts with is testing library.

Here’s an example of a much better approach:

1import React from "react"
2import { render, fireEvent } from "@testing-library/react"
3
4import HiddenMessage from "./hiddenMessage"
5
6test("shows the children when the checkbox is checked", () => {
7 const testMessage = "Test Message"
8 const { queryByText, getLabelText, getByText } = render(
9 <HiddenMessage>{testMessage}</HiddenMessage>
10 )
11
12 expect(queryByTxt(testMessage)).toBeNull()
13
14 fireEvent.click(getByLabelText(/show/i))
15
16 expect(getByText(testMessage)).toBeInTheDocument()
17})

Your test shouldn’t think in terms of props, classes, etc. It should think in terms of text, labels, button text, visible elements, and the things users see and interact with, and their associated accesible elements.

Writing tests this way, gives you confidence in your software. Because through this testing approach youre answering the question of “Does this code actually work?”

You can learn more about these types of testing methods by visiting Testing Javascript

UI Testing

UI testing is important for front-end developers.

Users only care about two things:

  • Being able to use your product to accomplish their goals ( Buying something, posting something, etc.)
  • To be DONE using your app so that they can move on

For example, no one enjoys ordering an Uber. You need to select a location, which type of car you’re going to ride, which payment method you want, etc. We enjoy the value we get from ordering an Uber. Which is arriving at our desired destination.

It’s also important to consider that roughly (at least) 50% of users will access your website on mobile. Testing for the mobile performance of your app is just as important as testing the desktop experience.

A cool tool for making sure that your app renders the exact way you want it to on all devices is Sizzy

sizzy demo

E2E Testing

E2E stands for End To End Testing, or the testing of the total product, not just its component parts (units).

We should not only care about individual components, but how they fit together. That’s why some people believe that unit tests are not important, when considering the end product.

More and more teams are moving towards automated tests.

A great testing framework is Cypress. It provides an interactive testing UI for developers to use.

Tomasz gives a quick demo at 23:30 where he tests a todo-app using Cypress.

Here are two great courses on egghead.io if you’re looking to learn more about Cypress:

Cypress can’t test what the user sees, it can see the DOM, CSS classes, but it doesn’t have eyes. This is where Visual Regression comes in.

Visual Regression

An awesome tool for visual testing is Applitools. It’s an AI powered visual testing & monitoring software.

applitools-workflow-visualization

This tool allows us to visually catch errors that we (the developer) might miss.

Here are what the tests for a login form look like in Applitools: example-applitools-tests

Here’s an example of a form component that will be tested using Applitools:

applitools-before

Applitools uses AI to detect any changes UI changes compared to the original version and will highlight the inconsistencies and their cause.

Here is the result of the above Applitools testing:

applitools-after

A great talk about Applitools is by Angie Jones, called “Your Tests Lack Vision: Adding Eyes To Your Automation Framework”

User Testing

the ultimate form of testing

Talking to your users is an important aspect of testing that is often overlooked *. This is to make sure that what you’re building is actually what they need.

Are your users finding value in what you’re offering?

The best way to find out is by making them use your software, asking questions and receiving feedback.

A great book about user testing is “Rocket Surgery made easy” by Steve Krug. It’s short and it teaches you how to talk to your users.

Key Takeaways

  • Care about your users and understand their needs.
  • The closer you are to the user (understanding their goals, their behaviors, etc.) === the better tests you’ll write. Understanding the user requirements is essential
  • Take care of yourself. No matter how many tests you have, things can still go wrong and that’s okay.

Don’t forget to watch the post-talk Q&A


Found this article useful? Make sure you share it with other people on Twitter

Hi! Welcome to my Blog 👋

This is where I write about the things I learn, Front-end Development and share my thoughts. I'm a Learner Advocate at Egghead.io and a Front-end Software Engineer in my 3rd year, studying Computer Science.