Register to get access to free programming courses with interactive exercises

Bad and good testing practices JS: Automated testing

Tests, like any other code, can be written in different ways, one of those ways is badly. In addition to some common practices and coding standards, tests have their own peculiarities that you should be aware of. In this lesson, we'll go over some of them.

Tests affecting each other

One of the key rules is that tests must not affect each other. This means that any test is performed as if no other tests existed.

It's very easy to break this rule. A single test can create a file, change a variable, or write something to a database. If the rest of the tests come across these changes, they may fail when they shouldn't fail, or, conversely, pass when they shouldn't. In addition, such a situation can introduce uncertainty. Tests can occasionally fail for no apparent reason. For example, when a test runs in isolation, it works, but when it runs with others, it crashes:

let user;

test('first', () => {
  user = { name: 'John' };
  // ...
});

test('second', () => {
  // The user that the other test created is used!
  // This test depends on how the previous test works,
  // and cannot work unless you run both tests in sequence
  user.name = 'Peter';
});

This situation can happen particularly often in tests that actively interact with the things outside the program, such as databases or file systems. Testing side effects has its own tricky moments, we'll look at it more in the advanced testing course.

Conditional constructions in tests

test('something', () => {
  if (/* something */) {
    // Execute the code in one way
    // The check may be here
  } else {
    // Execute the code different way
    // The check may be here
  }
  // The check may be here
});

Any branching within tests is actually several tests within one test. You should get rid of that and never write like that.

Tests outside of tests

The goal of beforeEach is to prepare the data and environment for testing, while the goal of test is to call the code being tested and perform tests. But sometimes developers overdo it:

let result;

beforeEach(() => {
  // The code being tested is called. This contradicts the idea of beforeEach.
  result = sum(5, 9);
});

test('result', () => {
  // Here it's only checking
  expect(result).toEqual(14);
});

In this example, the code being tested is called in beforeEach. This approach makes it difficult to analyze the tests because it turns everything upside down.

Too much detail

Programmers, influenced by advices from the Internet, tend to spread the code as much as possible across files, modules, and functions. The same is observed in testing. Instead of one test, which contains all the necessary checks, the programmer creates 5 tests, each of which contains exactly one check:

test('create user', () => {
  const user = { name: 'Mark', age: 28 };

  // Here's the code to add the user to the database

  expect(user.age).toEqual(28);
});
test('create user 2', () => {
  const user = { name: 'Mark', age: 28 };

  // Here's the code to add the user to the database

  expect(user.name).toEqual('Mark');
});

More often than not, this separation results in more code, and refactoring, later on, will be more complex because of the amount of code.

Deep nesting

Jest allows you to group tests into describe blocks:

describe('User', () => {
  test('should be valid', () => { /* ... */ });
});

They help you structure complex tests and assign each describe block to its own beforeEach. Although this feature can be useful, it's very easy to start using it to your detriment:

describe('', () => {
  describe('...', () => {
    describe('...', () => {
      test('should be valid', () => { /* ... */ })
    });
  });
});

An entrenched test hierarchy makes them difficult to analyze and keeps them bonded to one another. This complicates adding new checks. It makes it unclear what each test refers to. This is the problem with any hierarchies that view the system from only one point of view.

Code with tests takes longer to write than code without tests

This is a rather crucial topic, which you can use to test how good a programmer is at writing tests. Although some kinds of tests are quite complex and require extra time, the ordinary tests that are written along the code help speed up development. And there are five reasons for that:

  • Tests affect code design. They help identify bad decisions much earlier
  • Preparing input data can take a considerable amount of time. If you test as you go along, you only need to do something once
  • Checking the result of the code can be complex and varied. Tests mean that you don't have to worry about it, they do the checking themselves to make sure everything is okay, including borderline cases
  • If you write tests for you projects regularly, it's easier and faster to refactor, since you don't have to check other parts of the code manually
  • Tests reduce the anxiety of you doing something wrong

Are there any more questions? Ask them in the Discussion section.

The Hexlet support team or other students will answer you.

About Hexlet learning process

For full access to the course you need a professional subscription.

A professional subscription will give you full access to all Hexlet courses, projects and lifetime access to the theory of lessons learned. You can cancel your subscription at any time.

Get access
130
courses
1000
exercises
2000+
hours of theory
3200
tests

Sign up

Programming courses for beginners and experienced developers. Start training for free

  • 130 courses, 2000+ hours of theory
  • 1000 practical tasks in a browser
  • 360 000 students
By sending this form, you agree to our Personal Policy and Service Conditions

Our graduates work in companies:

<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.bookmate">Bookmate</span>
<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.healthsamurai">Healthsamurai</span>
<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.dualboot">Dualboot</span>
<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.abbyy">Abbyy</span>
Suggested learning programs
profession
Development of front-end components for web applications
10 months
from scratch
Start at any time

Use Hexlet to the fullest extent!

  • Ask questions about the lesson
  • Test your knowledge in quizzes
  • Practice in your browser
  • Track your progress

Sign up or sign in

By sending this form, you agree to our Personal Policy and Service Conditions
Toto Image

Ask questions if you want to discuss a theory or an exercise. Hexlet Support Team and experienced community members can help find answers and solve a problem.