In the realm of JavaScript and TypeScript development, testing your code is just as important as writing it. Jest, a delightful JavaScript Testing Framework, has gained immense popularity for its simplicity and feature-rich environment. When combined with TypeScript, it brings strong typing and helps catch errors early, leading to more reliable and maintainable code. In this article, we dive deep into how to use Jest with TypeScript, exploring its powerful features through practical examples.
Setting up Jest with TypeScript
Before harnessing the power of Jest in a TypeScript environment, you need to set up your project correctly. Start by installing TypeScript, Jest, and the necessary dependencies:
|
|
Then, initialize TypeScript in your project:
|
|
Configure Jest to work with TypeScript by editing your Jest configuration file (jest.config.js
):
|
|
Writing Your First Jest Test in TypeScript
With the setup ready, let’s dive into writing a basic test. Consider a simple function in sum.ts
:
|
|
You can write a test for this function in sum.test.ts
:
|
|
Run your tests using:
|
|
Exploring Matchers in Jest
Matchers are the heart of Jest’s testing power, allowing you to write various assertions.
Common Matchers
Matchers in Jest are methods that let you test values in different ways. These methods are used with the expect
function to assert whether a particular value meets certain conditions. Jest offers a wide range of matchers, allowing you to handle various scenarios in your tests, from simple equality checks to more complex checks like testing for exceptions or checking array contents.
toBe
andtoEqual
:toBe
usesObject.is
to test exact equality. It’s great for primitive types.toEqual
recursively checks every field of an object or array.
1 2 3 4 5 6 7 8 9
test("two plus two is four", () => { expect(2 + 2).toBe(4); }); test("object assignment", () => { const data: { [key: string]: number } = { one: 1 }; data["two"] = 2; expect(data).toEqual({ one: 1, two: 2 }); });
Truthiness:
toBeNull
matches onlynull
.toBeUndefined
matches onlyundefined
.toBeDefined
is the opposite oftoBeUndefined
.toBeTruthy
matches anything that anif
statement treats as true.toBeFalsy
matches anything that anif
statement treats as false.
1 2 3 4 5 6 7 8
test("null", () => { const n: null = null; expect(n).toBeNull(); expect(n).toBeDefined(); expect(n).not.toBeUndefined(); expect(n).not.toBeTruthy(); expect(n).toBeFalsy(); });
Numbers:
- Matchers like
toBeGreaterThan
,toBeLessThan
, etc., are used for comparing numbers.
1 2 3 4 5
test("two plus two", () => { const value: number = 2 + 2; expect(value).toBeGreaterThan(3); expect(value).toBeLessThan(5); });
- Matchers like
Strings:
toMatch
is used to check strings against regular expressions.
1 2 3
test("there is no I in team", () => { expect("team").not.toMatch(/I/); });
Arrays and Iterables:
toContain
checks if an array or iterable contains a particular item.
1 2 3 4 5 6 7 8 9
test("the shopping list has beer on it", () => { const shoppingList: string[] = [ "diapers", "kleenex", "trash bags", "beer", ]; expect(shoppingList).toContain("beer"); });
Exceptions:
toThrow
checks if a function throws an error when it’s called.
1 2 3 4 5 6 7 8 9 10
function compileAndroidCode(): void { throw new Error("you are using the wrong JDK"); } test("compiling android goes as expected", () => { expect(compileAndroidCode).toThrow(); expect(compileAndroidCode).toThrow(Error); expect(compileAndroidCode).toThrow("you are using the wrong JDK"); expect(compileAndroidCode).toThrow(/JDK/); });
Custom Matchers
Jest allows you to extend its matchers by using expect.extend
to add your custom matchers. This is useful when you want to add reusable test logic, or if you’re testing something specific to your application.
|
|
Matchers are a powerful part of Jest, providing a rich vocabulary for writing tests. They help make your tests expressive and ensure that your code behaves as expected. The variety and extensibility of Jest matchers make them suitable for a wide range of testing scenarios.
Setup and Teardown
Jest offers several functions that allow you to perform setup and teardown tasks in your tests. These functions are beforeAll
, beforeEach
, afterAll
, and afterEach
. They are particularly useful for preparing the environment for tests and cleaning up afterward. Here’s a detailed look at each of these functions:
1. beforeAll
This function runs once before all the tests in a describe block. It’s useful for setting up something that is needed by all the tests and is expensive to set up, like a database connection or a large dataset.
|
|
2. beforeEach
beforeEach
runs before each test in a describe block. This is useful for resetting conditions to a known state before every test, such as resetting database records or clearing mocks.
|
|
3. afterAll
This function is called once after all the tests in a describe block have completed. It is used for cleanup activities that need to happen after all tests have run, like closing database connections or freeing up resources that were used during the tests.
|
|
4. afterEach
afterEach
is executed after each test in a describe block. It’s often used for cleanup that should be performed after each test, like resetting mock functions or clearing changes made during the individual test.
|
|
Example Usage
Here’s an example that demonstrates how these functions might be used in a test suite:
|
|
In this example, initializeDatabase
and closeDatabaseConnection
are used for setting up and tearing down the database, which is a relatively heavy operation, so it’s done once. resetTestData
and clearMocks
are lighter operations needed to ensure each test runs in a clean state, so they are done before and after each test.
These functions help in organizing the test code and managing resources efficiently, making the tests more reliable and maintainable.
Wrapping it up 👏
Jest, when used with TypeScript, provides a robust framework for writing and managing your tests. Its rich set of matchers, combined with TypeScript’s strong typing, ensures that your tests are both powerful and maintainable.
Cheers! 🍺