Unit Testing - The easy way

Ahmed Moawad
11 min readJul 27, 2018

Umm, to be clear from the beginning, the above image talks about exactly the opposite thing of our story topic :D.

“ The act of unit testing is more an act of design than verification”
Robert C.Martin

But this quote talks exactly about our topic, thanks Uncle Bob.
So, if we treat unit testing as a coding style we will burst our code quality and maintainability.

In this story, we will go in details with how to integrate unit testing perfectly in your code with an easy way and make it become your sincere friend in your coding journey.

Note: I expect that the reader of this topic is familiar with jest or any unit testing environment setup. Click here to learn more about jest.

Let’s explore it together :)

Why Unit Testing ?

Many of us wonder about why using the unit testing and add more time to the coding time, here are some points that make us should use it in our code:

  1. Make you have a clear understanding of the Business.
  2. Identify the side effects of any change in your codebase easily.
  3. Make the collaboration easier.

Hello World Test

Let’s make a simple test with the basic element of testing

Sure, the above example is not a real unit testing example, but it contains the main units of it:
1. it is the Spec function that test a specific requirement on your business.
2. expect Expectations and toBe Matchers.

So it will be like that, On the bla bla Specification I expect something toBe equal to another thing.
So let’s explore more about the basic elements of Unit testing.

Specs

Specs are the functions that run in order to check if a specific requirement is achieved or not.

It takes two parameters:
1. Descriptive Details Message of the test case.
2. Callback Function that will run the test itself.

Notes:
1. The message must describe the test case
clearly and not in a verbose way.
2. Test case should test a
single requirement with one or more expectations.

Example:

Here we find the the checkHelloWorld function is a simple one that take a text and return true only if the text equal ‘Hello World’ statement. Let’s test that function.

Now, we have tested the case of that the function argument is Hello World should return true. and that obviously on the test message and will be covered on the callback function.

Suites

We can gather all related Specs in one single package called Test Suite.

There are many benefits for using suites:
1. Make your test more descriptive, modular and organized.
2. Put all the common setup and tear down commands in a single hook function. We will know how to do that later.
3. Give you the ability to skip the test of bulk of specs with single modification instead of skip every one separately.

Example:

As we see above, We test a function that return true if it is given ‘Hello World’ word and false otherwise.

So we describe our test as checkHelloWorld Function and we have in this suite two Specs to cover its functionality. It now looks more organized!.

Expectations and Matchers

Test without expectations is not a test.

Expectations are the elements that give us the ability to be sure that the unit that we are test works as expected.

Matchers are the utilities that make us able to compare the actual result with the expected one with various methods.

There are many types of matchers and we will show some of them here and you can explore more of them in Jest Docs.

We use the combination of the expectations and matchers like the following pattern:

expect(actualValue).toBe(expectedValue)

So let’s show some matchers that are useful in our work:

toBe → actualValue is expectedValue
it will work in comparing the primitive data types (strings, booleans, numbers) or if you want to compare that the two values have the same reference in objects.

toEqual → actualValue has the same elements of expectedValue
it will work in comparing the objects and arrays to have the same elements and their values are the same although they are not sharing the same reference.

const actualValue = {name: 'Ahmed', age: 12}
expect(actualValue).toEqual({age: 12, name: 'Ahmed'}) // Test Passed

toMatch → actualValue match a specific regex
it will work in comparing the strings against a specific regular expression.

expect('JavaScript').toMatch(/Java/)               // Test Passed :D

toContain → A specific array contains actualValue
it will work in asserting that an array contain an element.

expect(['BMW', 'Kia', 'Audi']).toContain('Jeep')     // Test Failed

Organize your work with Test Hooks

Test hooks gives us the ability to put the common setup and teardown stuff in a single place that runs before or after the tests.

Benefits of using Test Hooks:
1. Remove the redundancy of common code between the specs.
2. Make the specs code more readable and focus only on the spec logic.

beforeEach → a function that invoked before each Spec.
afterEach →
a function that invoked after each Spec.
beforeAll →
a function that invoked before all Suite specs.
afterAll → a function that invoked after all Suite specs.

Based on our simple test suite, We haven’t any setup or teardown stuff. But in a complex test suite you can reset your mocks, initialize variables or do any other stuff in order to prepare your test or clear its side effects.

What is Test Doubles

Test Doubles are generic terms for any case where you replace a production object for testing purposes with a simplified version of it.

Test doubles has many benefits in unit testing environment:

1. Make us focus on testing the unit functionality regardless to the side effects that surrounds the unit like Database, APIs and so on.

2. Give us the ability to track if any function get invoked inside the unit, how many times and what the parameters that have got.

3. Give us the ability to control the behavior of the real stuff that it replaced by like return a specific value if function get invoked, or module get imports and so on.

There are many types of test doubles and each one of them has a specific purpose, So we will discuss them on the following lines and know what is the difference between them, So let’s know what is types first:
1. Dummies.
2. Stubs.
3. Fakes.
4. Spies.
5. Mocks.

Note: In jest, Most of the test doubles called mocks because you can use it as a mock or any other type based on the implementation. But we learn them based on their common terminology.

Dummies

Dummy is a test double that used to replace the real element with just another one that doesn’t really do anything except replacing the real one in order not to break the test.

Validate Password Example:

Now Let’s know what we are going to test. We will test validatePassword functionality.
The question here, Is recordTry function affect from any side on the return value of validatePassword function?
The answer is No, because it just record the try of the user entries in the Database only.
So we should remove the side effects of inserting in Database in order to test the real logic of validatePassword function. Here we will use Dummy.

Test validatePassword Function using Dummy:

#1 here we have replaced the real implementation of recordTry with just an jest mock function (we will know what it is later, Now it just an empty function not only) that doesn’t do anything. So now when invoking it, it will just passed it because there is no implementation for it.

Note: In above example, we have replaced the real implementation with the mock function, but that’s not always the right way because in another part of the test we may need the real implementation. So we will know later how to restore the real Implementation.

Stubs

Stub is just a dummy but with an implementation so that we can use it in the test in order not to break the test because the unit expect and depend on the stub values.

validate Username Example

Now Let’s know what we are going to test. We will test validteUsername functionality.
The question here, Is validateUsernamefunction depends on checkIfUserIsUniquefunction in testing its functionality?
The answer is Yes, because it expect a value from it and in order to this value it will continue validating or stop it.
So in order to test that username first character is upper case we must consider that the username is unique. So we will create a stub of checkIfUserIsUnique that always returns true in order to test the other case.

Test validateUsername Function using Stubs:

#1 we have replaced the real implementation of checkIfUserIsUnique with a fake implementation that returns always true regardless to the input. So it will passed every time and let us test the other functionality easily.

Fakes

Fake is a test double that make a fake implementation of a dependency that used in the unit we tests like LocalStorage, Databases, Servers. So we can test the functionality with the conditions that we want.

localStorage Example

Now Let’s know what are we going to test. We will test recordTry functionality.
The question here, Is recordTry function depend on localStorage in testing its functionality ?
The answer is Yes, because it gets and sets the tries from and to the localStorage, So we need to make a Fake localStorage.

Fake localStorage Class

Let’s use it in our test:

#1 The test will not breakdown because we just make a fake simulated implementation of the localStorage. in order to test the functionality of recording the user tries.

There are many other things like that, like what called the Fake Server in sinon testing framework, it simulates and fakes the treatment with the endpoints and makes you get the desirable responses without interacting with the real endpoints.

Spies

Spy is a test double with the feature of the ability to track if the replaced item has been called, times of calling it, what is the arguments that it takes when it has been called. It’s a spy :D.

Clap Your Hand Example

Now Let’s know what we are going to test. We will test clapYourHand functionality.
The question here, Is clapYourHandfunction depends on console.logfunction in testing its functionality?
The answer is Yes, and we need to track if console.log has been called the times it should be called and the arguments that it takes every time is correct So we will spy on it.

Test clapYourHand Function using Spies:

#1 we have make a spy of log function with spyOn method to have the ability to track it after that.

#2 Now after calling clapYouHand function, we track the times of calling console.log inside it to be sure that it works properly.

Note: There are many other features that spies provide us in testing, and You can explore it in any framework that supports Spies.

Mocks

Mock is a stub with the features of Spies. So it have the ability to track the real object like spies and in the other hand it can give a determined values from it like stubs. So By Mocks we can track, fake and verify the behavior.

validate Username Example

Now Let’s know what we are going to test. We will test validteUsername functionality and track checkIfUserIsUnique function to assert that it takes the username as a param when get invoked.

Test validateUsername Function using Mocks:

#1, #2 we have here spy on checkIfUserIsUnique and force it to return true.
#3 We here verify that checkIfUserIsUnique is getting called with the correct params.

So Here we use the features of spy and stub to generate a mock that has been verified on the testing.

Now, We know about all the common types of test doubles in order to know when and how to use them. And I do that for the seek of that we can use another framework that using the test doubles with it’s terminology without calling all of them Mocks.

How to test Async Tasks

In testing environment, We have to wrap the async tasks in order to test them. In the next lines we will know their types and how to test them.

In JavaScript, We can implement the async tasks with various ways. Let’s explore them and know the correct way to test them.

Callbacks

callbacks are the classical way to handle an async task

Example

const getNames = (callback) => {
// Fetch names from server
// on getting the data send it to the callback
}

Testing Callbacks Example

What we did above is wrapping the async code, in order to test it.

#1, #3 done callback is the function that Jest will assert that getNames have returned, So it will start testing it and If the done method is never get called So the test will fail because getNames here didn’t get results and so on didn’t call its callback with the data.
#2 Run Expectations when done get called (the data is ready to be tested).
#4 Run the actual function in order to test it.

Promises

Promises are the current trend of making async tasks

Example

const getNames = () => {
return new Promise(resolveFn, rejectFn); // Dummy Promise
}

Testing Promises Resolving Example

What we did above is wrapping the async code, in order to test it.

#1 assertion here do what done function have done with callbacks, just to assert the promise is fulfilled.
#2 return here is required to complete the test after the promise completed.
#3 You can start make your expectations here.

Testing Promises Rejecting Example

here we test the case of promise rejection and actually we know that it will be rejected.

Note: There are shorthand methods for promises resolving and rejecting but it used only if we need to expect on the returned data only one time. here is an example for it:

return expect(getNames()).resolves.toBe(['Ahmed', 'Ali']);
// or on rejection case
return expect(getNames()).rejects.toBe('error');

async and await

async and await is the next way to make async tasks and wrap them perfectly

You can work with the async tasks using async and await ES8 new feature

Here we see that instead of return the promise and put the test logic in the then resolve or reject function, async/await feature give us the ability to treat the function as a normal one. So it gives us the ability to test the async functions and test it like normal ones. It’s awesome !

#1 Here we declare the test function as an async function.
#2 we use await to make getNames function fulfilled before starting tests.
#3 Here you can make your own expectations normally.

What Next ?

We have covered most of the basic and important fundamentals of unit testing like:
1. Expectations and Matchers and their various types
2. Test Specs and how it can be more organized using test suites.
3. Test Hooks and how it will prevent redundancy in your code base.
4. Test Doubles and what is its benefits, what are the various types of it.
5. Async Tests and how to achieve it.

The first step for you to master the unit testing is to practice it more and more and use it as code style, you can use the methodology of test driven development to achieve that faster.

There are many topics, that I think it will be useful for us:
1. Test Coverage.
2. TDD (Test Driven Development)
3. Snapshot Testing
4. Component Testing (for example in React, we use Enzyme)

At the end of this Article, I wish it was useful for you and have added value regarding to your valuable time.

Thank you and please contact me or comment if you have any feedback or want to contribute to enhance it.

--

--