The anatomy of a unit test

There are many different ways to test a piece of code, but in this chapter we will look at the anatomy of a test, what it is made up of. The first thing we need, for testing any code, is a test framework. The test framework should provide utility functions for building test suites, containing one or several test specs each. So what are these concepts?

  • Test suite: A suite creates a logical grouping for a bunch of tests. A suite can, for example, be all the tests for a product page.
  • Test spec: This is another name for a unit test.

The following shows what a test file can look like where we are using a test suite and placing a number of related tests inside. The chosen framework for this is Jasmine. In Jasmine, the describe() function helps us to define a test suite. The describe() method takes a name as the first parameter and a function as the second parameter. Inside of the describe() function are a number of invocations to the it() method. The it() function is our unit test; it takes the name of the test as the first parameter and a function as the second parameter:

// Test suite
describe('A math library', () => {
// Test spec
it('add(1,1,) should return 2', () => {
// Test spec implementation goes here
});
});

Each test spec checks out a specific functionality of the feature described in the suite description argument and declares one or several expectations in its body. Each expectation takes a value, which we call the expected value, and is compared against an actual value by means of a matcher function, which checks whether expected and actual values match accordingly. This is what we call an assertion, and the test framework will pass or fail the spec depending on the result of such assertions. The code is as follows:

// Test suite
describe('A math library', () => {
// Test spec
it('add(1,1) should return 2', () => {
// Test assertion
expect(add(1,1,)).toBe(2);
});

it('subtract(2,1)', () =>{
//Test assertion
expect(subtract(2,1)).toBe(1);
})
});

In the previous example, add(1,1) will return the actual value that is supposed to match the expected value declared in the toBe() matcher function.

Worth noting from the previous example is the addition of a second test that tests our subtract() function. We can clearly see that this test deals with yet another mathematical operation, thus it makes sense to group both these tests under one suite.

So far, we have learned about test suites and how to group tests according to their function. Furthermore, we have learned about invoking the code you want to test and asserting that it does what you think it does. There are, however, more concepts to a unit test worth knowing about, namely setup and tear-down functionality. A setup functionality is something that sets up your code before the test is run usually. It's a way to keep your code cleaner so you can focus on just invoking the code and asserting. A tear-down functionality is the opposite of a setup functionality and is dedicated to tearing down what you set up initially; essentially it's a way to clean up after the test. Let's see how this can look in practice with a code example, using the Jasmine framework. In Jasmine, the beforeEach() method is used for setup functionality; it runs before every unit test. The afterEach() method is used to run tear-down logic. The code is as follows:

describe('a Product service', () => {
let productService;

beforeEach(() => {
productService = new ProductService();
});

it('should return data', () => {
let actual = productService.getData();
assert(actual.length).toBe(1);
});

afterEach(() => {
productService = null;
});
});

We can see in the preceding code how the beforeEach() function is responsible for instantiating the productService, which means the test only has to care about invoking production code and asserting the outcome. This makes the test look cleaner. It should be said, though, in reality, tests tend to have a lot of setup going on and having a beforeEach() function can really make the tests look cleaner; above all, it tends to make it easier to add new tests, which is great. What you want at the end of the day is well-tested code; the easier it is to write and maintain such code, the better for your software.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset