When we write tests, as the good developers we are, we tend to stick to a few good practices, such as:
- Arrange, Act and Assert pattern for writing clearer tests.
- Test Data Builder pattern to simplify our Arrange.
- Grouping our tests into suites/fixtures to have common test data.
- Keeping it DRY with parameterised tests for different inputs.
To start off, we need a test framework that allows us to create tests and group them into suites. We need it to allows us to create common setup logic for a group of tests as well. The code snippet below is an example of writing tests using Mocha library. In this example we have a test suite, represented by the describe function, called “LoginForm: Verify Validation” with three tests, represented by the it function.
The following code snippet shows us how to create parameterised tests using Mocha.
Mocha promotes BDD-style assertions, which are assertions that can be read almost like a sentence. This is achieved using a technique called method chaining. Mocha’s default assertion library is ExpectJS. As with everything, we can easily replace it with a different assertion library as we see fit for the job at hand. The following code snippet is an example of assertions using Chai library, which has more chainable words than ExpectJS.
When it comes to unit testing, our focus is on testing a specific unit of work, known as System Under Test. The SUT can be one or more objects/components/actions that perform a unit of work. Our goal is to either verify a SUT’s state or its interaction with other objects/components. The following snippet is an example of simple unit test.
If that SUT uses other components and/or services to perform its job then including them in your test setup and execution will increase the scope of your test, known as scope creep. In such cases, we keep our test simple by mocking away the external dependencies. No, not in the sense of ridiculing them to the point they run away but more in sense of mimicking or imitating their behaviours.
Mocking is the art of replacing external dependencies with imitations that can simulate desired output to satisfy your test case. – Rajeev Nithiyananthan
Let’s add a twist to the previous example. The Warehouse now persist products in a database using a repository, rather than keeping them in-memory. The code snippet below shows what it would look like when we use Sinon to mock the external dependency.
What if we want to make sure that a specific method wasn’t called too many times without changing its behaviour or what if we want to verify that the method we mocked wasn’t called too many times? The following snippet highlights how this can be done using Sinon.
Test runners are essentially tools that wrap our test suites, in Mocha they are the describe functions, and run them in a specific environment.
Please keep in mind that you are not always going to need a separate test runner.
Some test frameworks, as with Mocha, are capable of running tests on Node.js when combined with the popular build tools, such as Gulp and webpack. These are great for running unit tests that do not require browser-based testing. The tests are effectively run in the terminal and the results are displayed in terminal as well.
When we are dealing with testing user interaction on a website, then we need to render it on a browser so that we can navigate thru the pages and verify the expected behaviour. To this end, we can use PhantomJS or Chutzpah, popular headless browsers. Headless browsers access web pages like any other browsers do but without showing the actual browser’s user interface. As with test frameworks running tests in terminal, headless browsers behave the same.
If the requirement is to run the tests on real browsers and in different environments, that’s when we will come across more advanced test runners such as Selenium WebDriverJs and Karma.
Popular Libraries for Testing
Useful article! It would be good to describe in next topics using Chai and creating tests for React + Redux applications.