Writing Automated Tests with Mocha and Chai


Many people claim that unit testing is a necessary condition for refactoring code. It is very hard to reliably refactor code without having good testing coverage. According to the definition of code refactoring, the process results in changes applied on the code without changing its external behavior. Good luck establishing a reliable refactoring process without automated testing.

In addition to taking refactoring into consideration, having automated tests is essential for developing commercial Javascript applications. Even if you develop open source components, people will only place trust in your code if it is thoroughly tested. The only commonly accepted and repeatable proof of thorough testing is a collection of executable automated tests.

Which testing libraries should I use?

The list is quite long. If you don’t have unit tests in your repositories, any reliable and actively maintained testing library can be useful for you. My suggestion would be to focus on making your testing process better instead of spending a lot of time on choosing the best testing library.

When I was included in making a decision about which testing libraries to use, it was evident to me that it was a battle between Jasmine and Mocha. The main reason why Mocha seemed to be a better choice was its modular nature. I prefer to afford the flexibility, extensibility and configurability provided by cooperating modular libraries. Mocha, accompanied by an assertion library and a mocking library does exactly that.

We will not focus on mocking libraries in this article. It is still worth mentioning SinonJs. Sinon is a great tool for creating spies, stubs and mocks to provide you with runtime information on the object under test, and simplify its dependencies. In addition, Sinon lets you take full control of time and all server communication using fake timers and fake servers. This way you don’t have to wait seconds or worst case minutes until the desired effect shows up, and you can also test your code in isolation, without communicating with the real server.

Assertion styles

Assertions describe your expectations towards how your code should behave. The most popular assertion library used with Mocha is ChaiJs. According to the style guide, ChaiJs offers three different assertion styles:

All three styles are easily readable. Although it is a matter of taste, in my opinion, the Should assertion style is often slightly more natural to read and interpret than Expect. The Assert style lags behind the other two in terms of readability, simply because they cannot be read from left to right as sentences. Assert describes the relation between two values in a prefix notation, starting with the relation, followed by the operands. If your team prefers reading > X 3 to X > 3, Assert will likely to be your choice.

Readability is not everything. Notice that the only way to write assertions with should, is that the assertion library extends the Object prototype. This could lead to errors in rare extreme cases like trying to call the non-existing should method of undefined. More importantly, an assertion style not extending the Object prototype is simply cleaner. This is a strong reason in favor of the Expect style. As Expect is very close to the standard Jasmine syntax, choosing Expect makes your assertions familiar to Jasmine users. For all these reasons, I will stick to the Expect style in the rest of this article.

I did not consider that the assertion styles are grouped by the ChaiJs style guide under the umbrella of BDD and TDD. This information is slightly misleading as any assertion syntax can be used for any testing processes. Recall my post on Test Driven Development describing why your software development methodology should include some strategy that lets you write tests independently from the bias of implementation. The choice of assertion library has little to do in establishing these processes.

Building Blocks

The top level building block is called describe, responsible for describing a test suite. Test suites can be hierarchical, see the two second level test suites inside the top level test suite. Each test suite can have two types of setup and tear down functions: one running before/after each and every test, and one running before/after all tests. These functions are important so that each and every test case can focus on a piece of functionality instead of long setup and cleanup code. More importantly, each and every test can and should run in isolation with the help of correct setup and tear down functions.

The tests contain a text description also appearing in the report. This description should describe the functionality under test in a compact sentence. The second argument of an it test case is a function containing one or more assertions that have to be verified by the code.

Notice the three pending tests. Tests cases without implementation or xit test cases are automatically pending. Don’t forget the advice of this article about failing tests: never ever accept them. If you are aware of a failing test that you cannot repair immediately, make it pending until you have the resources to make it pass.

Unit Testing advice

Many testers have trouble structuring and writing correct tests. The principles of writing maintainable code also apply for tests. I will enumerate some advice on writing maintainable tests:

I have read advice suggesting that you should write exactly one assertion per test case. This makes some sense given that if your first assertion fails, other assertions won’t even be evaluated. On the other hand, common sense often overrode this suggestion, especially when I had to test pieces of functionality acting as a lookup table. For instance, if your task was to check how a roman numeral to decimal converter converts single digits, you have the following choices:

Note that at an expense of readability, it would be possible to write just one assertion for the “it should convert single digits” test case as well. After all it is possible to come up with a boolean expression and check if that expression is true. This however only proves the point that it is not the number of assertions that should be limited, but rather the number of different pieces of functionality.

Assertions

If you have not written automated tests before, in order to take full advantage of this post, I suggest setting up Mocha and Chai yourself so that you would have a starting point for writing an application guided by tests.

The following lookup table will show how basic assertions can be written in Expect style. Feel free to copy-paste the assertions and run them in tests. Alternatively, download the examples of this section from this repository.

Let’s start with type checking using the a and an functions. Note that opposed to the typeof operator, null is not an object.

Chain objects such as at, be, been etc. can be written in any positions for the purpose of increasing readability.

Placing a not between the checked value and the checking function call negates the result. The assertion passes whenever the same assertion without not would fail.

Due to loose typing, we sometimes have to deal with conditions that are truthy. A value is troothy whenever negating the value twice becomes true due to the implicit type cast done by the negation operator. Truthy values are ok while falsy values are not.ok.

Equality is a fundamental assertion. In fact, most assertions can be expressed using equality and native Javascript, without significantly reducing readability. Equality works like the === operator meaning that both values and types should equal. Two reference types are equal whenever they point at the exact same object, array or function. Their contents do not matter.

When comparing reference values, we often use equality in a sense that all primitive values inside the compared objects in any depth should be equal to each other respectively. This is called a deep equality check, denoted by deep.equal or eql.

I generally like solutions that explain themselves even if the reader is not familiar with the used library. As the difference between eql and equal requires additional knowledge about Chai, I prefer using the longer, but self-explanatory deep.equal form.

Multiple checks can be chained using the and. Note that and does not shield its right part from the effects of not on the left. Therefore, applying and and not in the same assertion is discouraged due to readability reasons.

Beyond truthy and falsy values, true and false explicitly checks if a value is actually a boolean.

Checking against null and undefined was already solved in the very first assertion example of this section. As syntactic sugar, these checks can be shortened. In addition, exist checkes if a value is defined and is not null.

When checking the structure of an object or an array, we only know how to check deep equality so far. Sometimes we are just interested in the existence of a couple of keys and/or values inside the tested value. Property checks and deep property checks are possible in the following way:

Length is a special property deserving some syntactic sugar. Regular property checks are also available for checking length if that is your preference.

Property checks take the prototype chain into consideration. If you want to avoid the prototype chain and restrict property checks to own properties, use ownProperty instead of property.

The following assertions check if a value is a member of an array. This is purely syntactic sugar as a contain check can be expressed with comparing the value of an indexOf lookup with -1.

Especially, but not limited to serializing and unserializing JSON payloads when communicating with an API, checking the existence of each and every key is a very important test. It is even more important to check that no other keys are sent to the server as they may cause unwanted log lines or in some cases, the endpoint call may even be rejected. The have.keys check exhaustively checks all keys for exact matching, while the contain.keys check only checks the existence of the enumerated keys.

More syntactic sugar is coming, replacing the >, <, >=, <= and instanceof operators, matching arbitrary regular expressions, checking substrings in strings or satisfying an arbitrary function returning booleans.

An assertion fails whenever an error is thrown inside it. As we don’t want failing tests for checking erroneous states, expecting a function to throw an error is a valid use case for testing error states. There are obviously workarounds for throwing an error whenever a specific type of error is not caught in native Javascript, but the error check is more easily readable.

Preparing your cheat sheet

All the above examples are available in this test suite. The executed test suite can be viewed in a browser, where each test case can be expanded and collapsed in order to view the assertions inside the test cases.

Mocha-Chai-SinonJs CheatSheet

Mocha-Chai-SinonJs CheatSheet

Steps for preparing the test file:

  1. Clone the mocha-chai-sinon-cheatsheet repository.
  2. Execute npm install in the cloned folder
  3. Display index.html in a browser

If you would like to extend the cheat sheet, feel free to submit a pull request.

What is next?

Due to the length of this article, I did not deal with important topics in Mocha and Chai. In the second part, I will describe how to deal with asynchronous tests and I will also introduce fixtures. As a side effect, an example will exercise the before, beforeEach and after functions of test suites.

From the part onwards, we will deal with SinonJs. First we learn how to spy, stub and mock objects, followed by a demonstration of how to use a fake timer and a fake server.

If you liked this article, read the complete series on test automation.

Learn ES6 in Practice

Sign up below to access an ES6 course with many exercises and reference solutions.