Introduction to Automated Tests

(last update: )

Two women looking at code at the laptop while one of the women is pointing to a specific line of code

How do you know that your code works? You test it. That’s the only guaranteed way that your code works as it’s supposed to.

And how do you test it? You can access an endopint of your API with curl, Insomnia or Postman, or use Devtools of your browser to assert that the UI is rendering correct information.

This type of test ensures your code is working as it should right now, and that’s great!

But, what if you need to change the file of that endpoint, or just add an if statement when rendering a screen? It would definitely keep working as it should, right??

Well… sometimes it does, but most times it simply won’t. And that’s normal, especially on bigger codebases (not necessarily big, but bigger than a simple 1 table CRUD).

So how do you make sure it keeps working as usual? You test it. Manually. Again. Yay.

Yes you will have to test it again, but it sucks right? Having to manually make a request and manually check if the information of the response and the database info are correct according to the request you sent.

I got tired just by thinking about what I wrote in the last 2 paragraphs.

With that in mind, I’ll show you another way to assert that your code is still working.

Automated tests

Automated tests allows you to write code to test the code of your application. As a developer you might’ve already realized that the actual work of a developer involves automating repetitive tasks. You can think of automated tasks just as a way to automate all the repetitive and manual tests you do when you test it manually.

There are many ways software can be tested and you’ll encounter many names, for example:

  • when you’re testing an isolated file or feature it’s called unit testing
  • when you’re testing multiple components that are integrated, it’s called integration testing
  • when you’re the whole system just like it’ll be done in production (e.g. simulating user inputs, browser navigation), it’s called end-to-end or e2e testing

But, for purposes of better introducing you to a more fundamental approach of automated tests, I’ll be demonstrating with unit tests.

Going back to automating stuff, have you asked yourself what exactly are you automating when writing tests?

Basically, the phrase you have to keep in mind when automating is the following:

Based on a specific environment of your software, you take action just like the user would have and you verify that the code works as it’s supposed to.

With that in mind, let’s dive a bit deeper on the test structure.

Test structure

Let’s say you have a simple testsAreCool function inside testsAreCool.ts that just returns a string:

function testsAreCool() {
  return "Tests are cool!";
}

Usually, a test file is often called the name of the feature or file that is being tested and a .spec or .test. Considering the example above, testsAreCool.spec.ts would look something like this:

import { testsAreCool } from './testsAreCool';

describe('testsAreCool function', () => {

  test('should return the correct string', () => {
    const result = testsAreCool();
    expect(result).toBe("Tests are cool!");
  });

});

The structure can be explained as:

  • describe specifies a test suite, which is a collection of tests
  • it (can be replaced with test) represents the individual test
    • the it here can be understood as “how it should behave?”
  • there can be multiple individual tests inside a test suite

Write good tests with this pattern

Let’s say you have a Calculator class:

// src/Calculator.ts
export class Calculator {
    multiply(a: number, b: number): number {
        return a * b;
    }
}

And the following test file:

// tests/Calculator.test.ts
import { Calculator } from '../src/Calculator';

describe('Calculator class', () => {
    let calculator: Calculator;

    beforeEach(() => {
        calculator = new Calculator();
    });

    it('should multiply two numbers correctly', () => {
        const a = 4;
        const b = 5;

        const result = calculator.multiply(a, b);

        expect(result).toBe(20);
    });
});

Did you find any pattern on this test, a pattern that you do (consciously or unconsciously) when you’re testing manually?

If you haven’t, let me clarify:

  1. Arrange and setup everything that is needed for the test to run, in the example above, it’s the declaration of a and b and the beforeEach function
  2. Act accordingly to trigger actions expected, in the example above, we assign calculator.multiply() to result
  3. Assert everything works as expected, in the example above, we verify that result is equal to 20 with the expect function.

Now you no longer have to debug your code and provide all the inputs and all the configuration every time you have to test it.

And most importantly, you have solid proof that it works the way you expected. If the code needs more funcionality or it has a new bug? You fix the bug or implement the new funcionality and create a test for it, so the next time it won’t appear.

Besides that, some people argue that it’s even better to write the test suite first, then the code itself, that’s the idea behind Test-Driven-Development.

A word on productivity

Of course, there’ll be times when you just need to ship faster and writing tests for you sum(a, b) function is definitely not worth it. But let this talk of “To Test or Not To Test” for some other time. For now we shall praise the automation of a repetitive task!

But if you’re really interested in productivity, ping me and we can talk more about it (and maybe you can help me write more about it!)

A word on assertiveness

I’ve also mentioned that you can also enhance assertiveness of your code, and that’s because writing tests assume that you know what’s right and what’s wrong about what you’re coding.

Code assertiveness enchances because understanding your code becomes a consequence of finding all the caveats of your code when writing tests, like edge cases, for example.

Conclusion

I truly hope grasped the core idea of automated tests and how powerful and efficient they can be.

Make sure to contact me if you have any suggestions, recommendations, tips, questions or if you just want to have philosophical conversations!

Reference