Test your TypeScript Code with Ease

Alberto Basalo
JavaScript in Plain English
6 min readApr 23, 2023

--

Testing helps you catch bugs early, ensure quality, and confidently deploy solutions. The good news is that testing with TypeScript could be really easy. Trust me, the peace of mind is worth the effort.

With a solid understanding of testing principles, you can become proficient in testing TypeScript code in no time. This guide will teach you to test your TypeScript code with Jest and sleep better.

A relaxed dev after testing its code.

πŸŽ’ Prerequisites

To complete this tutorial, you will need the following:

Enough talk; let’s go with the four tips you need to write better tests for your code.

βœ’οΈ Write meaningful test descriptions.

A test is a program that produces a boolean result indicating whether it passed or failed. But more than a concise response is needed to understand what is happening.

This extra info is written in the test descriptors strings. Think about your test as a program whose primary data are the descriptions it writes when executed.

Those descriptions should be clear, concise, and up-to-date.

βœ… Conventions

Explore standard naming conventions for test cases and follow them to make your tests more consistent and easier to read.

  • Use the first describefunction to set the subject under test.
  • Use nested describes to set a scenario for a role-play
  • Follow the intuitive name of the if function with a should
  • Try the Given - When - Then pattern for high-level (integration or e2e) tests.

❓ Test descriptions should respond to three questions:

What are you testing?

Under which circunstancies?

What is the expected result?

To do so, you can use the following structure:

describe("The subject under test", () => {
describe("in this scenario", () => {
it("should produce the expected result", () => {
// test code
expect(result).toBe(expectedResult);
});
// any other expectations
});
// any other scenarios
});

πŸ–₯️ Write clean test code.

Test code, like production code, should be maintained. Write readable code that follows best practices, such as using descriptive variable names and avoiding complex expressions.

Use simple and concise language in your test cases to make them easy to understand and follow.

βœ… Organize your testing instructions.

Follow the AAA pattern to organize your tests. This makes your tests easier to read and understand. Group your test instructions into three sections:

  • Arrange: Set up the test environment and prepare the data as described in the scenario. To avoid repetition, this code could be reused in specific functions like beforeEach or abstracted in utility modules.
  • Act: Simulate any code flow by calling the subject under test. Ideally, a single instruction produces the result.
  • Assert: Verify that the result matches the expected behavior. Leverage the expressive power of the assertion library to make your tests more readable.

βœ… Choose names that make sense.

Name your variables and functions so others can quickly understand what they do. Adhere to a standard naming convention to make your code more consistent and easier to read.

For example, start using actual or result and then be more specific, such as actualClient or resultId.

It’s the same about too generic names like user or client; be more precise, such as userWithValidEmail or clientWithInvalidEmail.

βœ… Use test-specific data

Use data particular to the test case and reduce global test data that can be hard to maintain. To extract and reuse data, determine a dedicated place for your sample data.

Values should be realistic, so forget about foo, bar, John Doe, and the like. Treat the data as code.

❓ Clean test should respond to these two questions:

How does this test work?

Makes sense for the entire team?

Use the following structure to make your tests more readable:

it("should create an user with the desired name", () => {
// Arrange
const inputUser = {
name: "Alberto Basalo",
web: "https://albertobasalo.dev",
};
// Act
const createdUser = createUser(inputUser);
// Assert
expect(createdUser.name).toBe(inputUser.name);
});

πŸ§‘πŸΌβ€πŸŽ“ Test behavior, not implementation.

A common pitfall and a source of frustration for many developers is testing implementation details instead of behavior, making tests brittle and prone to break while refactoring.

This practice goes against the testing goal: having code easy to change that works as expected. Test the behavior of your code, not its implementation, to make your tests more robust and less likely to break when you refactor your code.

βœ… Black-box testing.

It is easier and less tedious to test only the public interface of your code. Thinking in terms of expected behavior helps you focus on what the program does and makes your tests more robust when you change the internals.

βœ… Use stubs and spies.

Even testing the public interface requires private interaction. Commonly, you must go deep and handle your code dependencies. To do so, it is recommended to use doubles instead of the real collaborator. The tip is to avoid going directly to create complex mocks and prefer using stubs and spies.

  • Use a spy to check that your code calls the proper functions with the correct arguments.
  • Use stubs to simulate the behavior of a dependency in a concrete situation.

❓ Any useful test should respond to the following questions:

What is the expected behavior?

What is the actual result?

Do they match?

πŸƒπŸΌβ€β™‚οΈ Run tests often.

Testing should be a habit, part of your development process. DonΒ΄t wait until the last minute to test before deployment. Chances are that you will be busy or stressed, and you will not be able to focus on testing.

So, run your tests often, ideally after every change, to catch errors early and prevent regressions.

The tests must be fast to not interfere with your development process. Follow these guidelines to help you write and run tests:

βœ… Small, focused tests.

Keep each test case focused on a single unit of functionality or behavior to make them easier to read, write, and maintain.

It is better to have many small tests than a few large ones.

βœ… Isolate tests.

Isolate each test case to prevent side effects and costly operations. Sure, at a certain point, you may need to read or write to the file system or make network calls, but try to avoid it as much as possible.

βœ… Allow testing in parallel.

Running tests in parallel will speed up the test process. But be careful not to write tests that may depend on each other, so make sure your tests are isolated. (See the previous point.)

Code and tests have to evolve together. So, make sure your tests are up to date and reflect the current state of your code.

βœ… Mental model

  • Group your tests into logical units to make it easy to understand what scenario is being tested.
  • Each test should be focused on a single unit of functionality or behavior.
  • Use a naming convention for your test files and folders to simplify running them selectively.
  • The scenarios and behavior descriptions should be clear and concise.

❓ From time to time, ask yourself the following questions:

Is the current code working as expected?

Are the tests stale?

πŸŒ… Summary.

You learned how to test your TypeScript code with Jest in this article. Now, you can write tests that are easy to read and understand and help you catch errors early while preventing regressions.

Those are my four tips for easy testing:

  • βœ’οΈ Write meaningful test descriptions.
  • πŸ–₯️ Write clean test code.
  • πŸ§‘πŸΌβ€πŸŽ“ Test behavior, not implementation.
  • πŸƒπŸΌβ€β™‚οΈ Run tests often.
4 tips for writing good test

As a final piece of advice, one test is better than none. A good test is better, easier, and more pleasant to write and run. So, start writing good tests for your code today. You will be glad you did.

❓Ask me if you need help getting started: https://albertobasalo.dev/#contact

learn, code, enjoy, repeat

In Plain English

Thank you for being a part of our community! Before you go:

--

--

Advisor and instructor for developers. Keeping on top of modern technologies while applying proven patterns learned over the last 25 years. Angular, Node, Tests