In the era of agile development and continuous integration, it’s crucial for us developers to test our code not only once, but repeatedly. Popular agile methods like test-driven development (TDD) and behaviour-driven development (BDD) rely on testing as a fundamental building block.
So why are tests so central to modern programming? Each program is susceptible to bugs, some of which might show up only after the program’s been deployed. By testing our modules independently and in as many scenarios as possible, we minimise the danger of having to revise entire programs. Tests ensure that we find and fix our bugs early in the development cycle, saving us both time and money.
A nice side-effect of tests written in TDD and BDD is that they’re self-documenting. By reviewing the corresponding tests, any team member can quickly understand what a piece of code is (or at least should be) doing!
Of course it’s possible to write some of your tests directly in the JS framework of your choice. But you’ll soon get tired of having to write the same boilerplate code over and over again. There’s a much quicker and more elegant way of dealing with tests, and that’s through the use of specialised tools.
What types of tests are there?
Unit tests check the performance of one isolated function or class. Since most programs consist of interactions between modules, isolating a function isn’t trivial. As we’ll see later, JS test procedures offer a number of ways to deal with this issue. Unit tests are further divided into server-side (on the back end) and client-side (on the front end).
Integration tests act as the complement to unit tests. They help us test the way that several components interact. It’s possible that all the components of an integration test have passed our unit tests—but once when we put them together, they don’t integrate as we had expected. Integration tests identify such cases.
Finally, end-to-end (E2E) tests allow us to simulate a full user experience that invokes both the front and back ends of an application. An E2E test usually simulates a user interaction with the application by automating actions like mouse clicks and keyboard input in a web browser. E2E tests are the most complex in the testing cycle and are addressed by special test frameworks.
What test components are there?
The actual test that checks whether a piece of code does what we expect of it is called an ‘assertion.’ When our code turns out true (that is, it behaves the way we want), assertions pass quietly. Faulty code, on the other hand, will cause an assertion to throw an error and alert the developer to a bug in the code.
To deal with external dependencies, we use ‘test doubles’ (a play on ‘stunt doubles’). With test doubles, we may prepare a canned response from, say, a database, that allows us to test whether our code is able to deal with a given value as it should. You’ll often see a distinction between mocks and stubs, but they really both fulfill the same purpose of isolating your code from the outside world.
Sometimes we want to have some meta-information about a test itself. For example: how many times did a function actually run within a given test? To do that, we’ll employ ‘spies.’ A spy wraps the function and returns information about it, telling us more about the inner workings of our code.
So once you have all of your assertions, mocks, and spies in place, does that mean that your test pipeline is complete? Not quite. After all, you’ll want to make sure that you’re really testing all the functionalities of your code. A helpful metric is test coverage: special libraries that check how much of your code is actually covered by your test. For example, if your test only ever tests the if-condition of an if-else statement, how do you know that your program won’t fail when encountering a new condition? While test coverage isn’t a catch-all, it definitely helps to aim for a good score here.
Server-side unit and integration tests
Mocha is a test framework for Node.js applications. Asynchronous tests (when one component has to wait for another one to issue a command) are an important feature in JS testing, and it’s easy to perform them using Mocha. The framework can combine different kinds of assertion libraries, but its flexibility means that it takes a little longer to set Mocha up compared to other frameworks.
A slightly older framework, Jasmine was explicitly designed for testing in BDD. Like Jest, Jasmine is easy to set up and comes with in-built functions for assertions and test doubles. It’s the preferred framework for testing in Angular.
Sinon is a mocking tool. This library takes care of your mocks, stubs, and spies. Like the Chai assertion library, it’s usually imported into another test framework.
Client-side unit tests
In your E2E tests, you combine both the back and front end to verify that your applications really work. You might want to simulate an entire user experience, such as signing up for a service or changing a password. Traditionally, E2E tests have been pretty time-consuming. Thankfully, we now have Cypress, an E2E test framework designed specifically for developers. Cypress is fast and simple to use. We like it so much that we’ve written an entire blog post about Cypress—check it out if you want to learn more about this E2E test framework.
In last year’s survey, more than 23,000 developers gave their input. Results are categorised by ‘awareness’, ‘interest’, ‘usage’, and ‘satisfaction’. While the first two categories simply describe whether or not people know about a certain framework and think about using it, the latter two reflect actual usage statistics:
Checking to see what your peers are using is great, but there are other factors to consider as well. Oftentimes, your entire setup and use case will limit the choice of test framework. For instance, if you’re a developer wanting to do end-to-end tests, you’ll probably want to go for Cypress. If on the other hand, you’re looking to test your Node.js applications, you’ll be looking for a server-side unit testing framework, such as Mocha or Jest.
Other times, one choice of framework will dictate what other tools to use. For example, most people who use Mocha for handling their test flows combine it with the Chai assertion library and the mocking tool Sinon for easy integration.
Start testing with Mailosaur
No matter your choice of test framework, integrating the either the Mailosaur Cypress plugin or Node.js library into your tests is quick and easy. Sign up for an account to use Mailosaur for email testing in your JS code.