Once I was asked to talk about unit testing in javascript, but before talking about testing in the front-end world, I had to do a little review of unit testing as such. As a result, this article came into being, in which I tried to talk about the most important points in unit testing.

Despite the different interpretations of unit testing, there are several things that unite this term.
But there are moments in the definition of unit testing, which are still controversial. In particular, what is considered a unit (unit of testing)? The OOP approach considers a class as a unit, a procedural (or functional) approach, treats one function as a unit. Some developers take several classes and consider it a unit, or take a set of methods as a unit. But in reality this is a situational thing, the team decides for itself what should be the unit of testing in their system.
')
The advantages of unit testing are obvious:
- They are low-level and focuses on a small piece of software.
- Tests are written by the developers themselves.
- Tests are performed very quickly, you can run tests several times a minute.
- When developing, you can perform not all tests, but only those that are necessary for you
Thus, when using unit testing, the development speed is not significantly reduced, but the quality of the product itself increases.
The important difference in unit testing is what type of testing you choose: Solitary (lonely) and Sociable (sociable) test. The terms were first introduced by Jay Fields.
A sociable (outgoing) test is a test that uses real methods (or classes) that are included in the unit under test. For example, you are testing the “price” method from an order class. The price method needs to call methods from the client and product class. In this type of test, these methods will be called, and an error in these methods will lead to a test error. Methods from the client and product classes are called collaborators.

A solitary test is a test that uses duplicates (
TestDouble ) as partners. Test duplicates are a generic term for any case in which you replace a real object, solely for testing purposes.

Gerard Meszaros (Gerard Meszaros) made a good classification of doubles. You can read more about this
here.Each of these testing methods has its advantages and disadvantages, and there are heated debates between supporters of these two methods. Supporters of Solitary (lonely) tests are also conventionally called Mock-ists (Mock is fake), and supporters of Sociable (sociable) tests are conventionally called Classicists (
could not find analogues in Russian ). It should be noted that supporters of Sociable (outgoing) testing also use double tests to access external resources, for example, to the database. In part, this is done because of the access speed. But using duplicates to access external resources is not an absolute rule, if access to them is stable and fast enough, then you can do without duplicates. In any case, the developer himself decides when it is better for him to use doubles.
One of the advantages of the Solitary testing technique (lonely) is that developers focus on the behavior of the application, and not on the state. The disadvantage is that the fakes can mask the error that is present in the partner method. Therefore, when using test duplicates, it is necessary to perform integration testing. The advantage of Sociable testing is that this is essentially the initial integration testing, but the disadvantage is that if one method falls, it will lead to a drop in all tests related to this method, which makes debugging difficult.
I will not dwell on the advantages and disadvantages of this or that approach in testing, you can read about it in Fowler in the article
Mocks Aren't StubsThe basic properties of unit testing are a small amount made by the programmer himself, and speed — which means that they can be performed frequently during programming.
Developers can execute them after any change in the code. But it is not necessary to always run all the tests, it is enough to run only those tests that interact with the code you are currently working on.
In the late 1990s, Kent Beck developed the Test-Driven Development (TDD) technique as part of extreme programming. This technique for building software that manages the development process through writing tests. In essence, repeats three simple rules:
- First a test is written.
- Then the code is written for this test.
- Refactoring new and old code to improve code quality
The process begins anew until you get the desired result.

Writing a test first gives you two advantages:
1. This is a way to get self-testing code.
2. Thinking first about the test, you force yourself to think about the interface of the code itself. This focus on the interface and how you use the class helps you separate the interface from the implementation.
The biggest mistake when using this methodology is the neglect of the third step, refactoring. This causes the code to be “dirty” (but at least there will be tests).
BDD (Behavior Driven Development) or behavior-based development, appeared in the process of unit-testing evolution and was developed by Dan North (Dan North) in 2006. According to the author himself, the methodology should help people learn TDD. It came from an agile practice and is designed to make them more accessible and effective for newcomer teams in Agile.
Over time, BDD began to embrace a wider picture of agile analysis and automated acceptance testing.
This led to the fact that the tests themselves began to rename the behavior (specifications), which allowed focusing on what the object needs to do. Thus, developers began to create documentation for themselves and write down the names of tests in the form of sentences. They found that the created documentation became available to business, developers and testers.
Behavioral development is considered to be one of the branches of the Mock-style (or Solitary-test), i.e. Tests are mainly built using duplicates.
Later, the writing style of the Given-When-Then tests, or, as it came to be called, the specification of system behavior, appeared. The style was developed by Dan North (Dan North), together with Chris Mattis (Chris Matts). The idea is to break test case writing into three sections:
- Given (Given) - state, before you begin to describe the behavior. can be considered as a prerequisite test.
- When (When) - the behavior that you describe.
- Then (Then) - the changes you expect from the behavior
Example:
Description: The user sells the stock.
Scenario: User asks for sale before closing
Given (Given): I have 100 MSFT shares and 150 APPL shares and the time until the close of the auction.
When (When): I ask to sell 20 shares of MSFT
Then (Then): I must have 80 MSFT shares and 150 APPL shares, and the application for the sale of 20 shares must be completed.Despite the fact that since the advent of TDD and BDD methodologies, a lot of time has passed, many developers still argue with each other about the feasibility of their use. Someone argues that there is no need to write tests before the code, others say that writing tests after the code is meaningless. But both sides agree on one thing: tests should be written! Methodology of BDD from the point of view of programmers, as its author claims (
BDD IS LIKE TDD IF ... ), does not differ from TDD. It uses all the same rules as in TDD: test, code, refactoring. The difference is that BDD covers a wider audience. Specifications are available not only to programmers, but also to people who do not understand the code, but are related to software development. Thus, the whole team is involved in the process of creating tests: analysts, testers, managers.
One obvious advantage of unit tests is that they can drastically reduce the number of errors that get into the product. At the core of this is a
culture , as a result of which developers think about writing code and tests together.
But the biggest advantage is not just to avoid errors in the product, but to make sure that you can make changes to the system. Old code is often a terrible picture where developers are afraid to change it. Even fixing one mistake can be dangerous, because You can create more bugs than correct. In such cases, adding new features is very slow, you are also afraid of refactoring the system, thereby increasing technical debt (
TechnicalDebt ) and you find yourself in a bad spiral, where every change causes people to fear even greater change.
With tests another picture. Here, people are sure that fixing errors can be done safely, because if you make a mistake, the error detector will work, and you can quickly recover and continue. With this security system, you can always keep the code in good shape and no longer find yourself in a bad spiral.
The error detector (self-testing system) is the process of executing a series of automatic tests (not just a unit), and you are sure that the tests will pass and your code does not contain significant defects. If someone on the team accidentally makes a mistake, the detector will trigger. By performing tests often, several times a day, you can detect errors immediately after they appear, so you can just look at the latest changes, which makes it much easier to find errors. No program episode is completed without a working code, and tests that support its work.

The system under test is part of
Continuous Integration (Continuous Integration) and Continuous Delivery (Continuous Delivery), but this topic is already beyond the scope of this article.
One of the important actions of a team that practices various tests is a reaction to a product error. The usual reaction of the team is to
first write a test to expose the error , and only then try to correct it. Often the writing of this test will be a series of tests that gradually narrows the scope until you reach a unit test that emulates an error. This technique ensures that after correcting the error, it will remain fixed. The position should be that any mistake is not just a failure in the code, it is also a failure in the protection of testing.
Not only unit tests, but also integration tests, and other automatic tests act as a detector of errors or automatic tests. But unit tests here play the basis, because writing them is easy and they are very fast.
High-level tests are the second line of defense. If you get an error in high-level testing, it is not just an error in the code,
it is a missing or incorrect unit test!List of sources:
- Martin Fowler UnitTest
- Martin Fowler TestPyramid
- Martin Fowler SelfTestingCode
- Martin Fowler TestDrivenDevelopment
- James Shore The Art of Agile Development: Test-Driven Development
- Introduction to Behavior Programming (BDD)
- Martin Fowler GivenWhenThen