Cover ImageTesting JavaScript Applications
da Costa, Lucas Fernandes
Manning Publications Co. LLC

73 notes/highlights

Created by Ahmed Mansour Ouda   – Last synced July 31, 2022


Mike’s pyramid is, in general, an excellent mental framework. Separating tests into different categories is instrumental in determining how many of each type we should write and how often they should run. But I find it problematic to divide tests by their target, be it a function, service, or interface.
December 11, 202143

Instead of dividing tests by their targets, I suggest that we separate tests by how broad their scope is. The larger the portion of your software a test makes up, the higher it will be placed in the pyramid.
December 11, 202143

the level of isolation of each test as the main criterion for its division. The new labels are as follows: End-to-end tests, Integration tests, and Unit tests
December 11, 202143

Unit tests Integration tests End-to-end tests Target Individual functions Observable behavior and the integration among multiple functions User-facing functionality
December 11, 202144

Quantity Numerous—several tests per function Somewhat frequent—many tests per observable behavior Sparse—a few tests per feature
December 11, 202145

Speed Very quick—usually a few milliseconds Average—usually up to very few seconds Slow—usually up to many seconds or, in more complex cases, minutes
December 11, 202145

Execution frequency Numerous times during the development of a function Regularly during the development of a feature When features are complete Feedback level Specific problematic input and output for individual functions Problematic behavior Incorrect functionality
December 11, 202145

Unit Tests: Costs Cheap—usually small, quick to update, run, and understand. Integration Tests Costs: Moderate—medium-sized, reasonably fast to execute. E2E Tests Costs: Expensive—take a long time to run, and tend to be more flaky and convoluted
December 11, 202145

Knowledge of the application Coupled—require direct access to the code itself; address its functions Address functionality, but also through direct access to the code; require access to components like databases, the network, or filesystems As unaware of the code as possible; interact with the application through the interface given to its users
December 11, 202145

NOTE Remember not to be too concerned about fitting tests into one category or another. The pyramid exists as a mental framework for you to think about the different types of guarantees you want to create around your software. Because every piece software is different, some pyramids may have a narrower base or a wider top than others, but, as a general rule, you should strive to keep the pyramid’s shape.
December 11, 202146

Node.js itself ships with a built-in module, called assert , to do those checks, which, in the context of tests, we call assertions. It contains functions to compare objects and throw errors with meaningful messages if the actual output doesn’t match what you expected. NOTE You can find the documentation for Node.js’s built-in assert library at https://nodejs.org/api/assert.html.
December 11, 202149

Example for (Arrange-Act-Assert Pattern): Arrange: creates an empty cart ❸ Act: exercises the addToCart function ❹ Assert: checks whether cart contains the newly added item
December 11, 202152

In Jest, you perform deep equality checks using toEqual , and strict equality checks using toBe . Read Jest’s documentation for the toEqual matcher to learn more about how it works. It’s available at https://jestjs.io/docs/en/expect#toequalvalue.
December 11, 202153

That ^ indicates that when running npm install , NPM will install the latest major version of Jest. In other words, the leftmost version number will not change.
December 11, 202153

In theory, when following semantic versioning practices, any nonmajor upgrades should be backward-compatible, but, in reality, they are not always. To force NPM to install an exact version of Jest when running npm install , remove the ^ .
December 11, 202153

Unit tests help you iterate confidently, by providing quick feedback as you write code, as we will see in detail when we talk about test-driven development in chapter 9. Because unit tests’ scope is limited to a function, their feedback is narrow and precise. They can immediately tell which function is failing. Strict feedback like this makes it faster to write and fix your code.
December 11, 202155

NOTE Because this book is about tests and not about databases, I’ve opted for the most straightforward possible database design. To learn more about database systems, I’d highly recommend Fundamentals of Database Systems, written by Ramez Elmasri and Shamkant B. Navathe (Pearson, 2016).
December 12, 202157

NOTE In testing lingo, tests that don’t know about an application’s internals are called black box tests. Tests that do are called white box tests. Tests don’t necessarily need to fit entirely in one category or another. The less they rely on an application’s implementation details, the more “black box” they are. The opposite is valid for more “white box” tests
December 19, 202165

With the rise of automated tests, the demand for manual QA has been decreasing dramatically. This isn’t because having a specialized QA team is not useful, but because some of their tasks, when automated, can be cheaper, quicker, and more precise.
December 19, 202173

Wedding cakes are one of the most expensive pieces of carbohydrates someone will ever buy in their lives. It’s challenging to pick one, and it’s even more stressful to worry about it until it arrives on the very day of your wedding.
December 19, 202174

This kind of acceptance testing is—at least for now—almost impossible for a machine to do.
December 19, 202174

So far, our comparison between QA professionals and machines has been pretty unfair. We have been comparing what computers are good at with what humans are the worst at: performing repetitive tasks quickly and flawlessly. A comparison that would be more favorable to users is in regard to creative tasks and empathy. Only humans can think of the multiple curious ways someone would find to use a feature. Only people can place themselves in someone else’s shoes and think about how pleasing a piece of software is.
December 19, 202174

TIP Patrick McKenzie wrote a brillant blog post about the intersection between the economics of businesses and software engineering. It’s a classic that I highly recommend and can be found at https://www.kalzumeus.com/2011/10/28/dont-call-yourself-a-programmer.
December 20, 202177

The next step to cut the cost of your tests is to reduce duplication in them. When you notice repetitive patterns, don’t be afraid to create abstractions.
December 20, 202178

Besides keeping tests readable and avoiding duplication, another crucial attitude to decrease tests’ costs is to make them loosely coupled. Your tests should assert what your code does, not how it does it. Ideally, you should have to change them only when your application presents behavior that’s different from what the test expected
December 20, 202180

You want tests to fail only when a function’s observable behavior changes.
December 20, 202181

Transitive guarantees are a great way to decrease the cost of your tests. They work in the same way as abstractions—they decrease coupling. Instead of all tests repetitively checking the same behavior, they delegate that responsibility to another test
December 20, 202183

3 Testing techniques


When testing this function, you must be careful not to create an execution path that could lead to no assertions ever running.
January 15, 2022105

The tighter and most valuable assertion you can write is an assertion that allows only a single result to pass
January 15, 2022109

Writing tighter assertions makes it harder for your tests to pass when the application code has problems, making it easier to catch bugs.
January 15, 2022110

Deterministic code A code is said to be deterministic when, given the same input, it always produces the same output.
January 15, 2022110

Asymmetric matchers can perform many different kinds of verifications. They can, for example, check whether a string matches a regular expression or whether an array contains a specific item. Check Jest’s documentation to see which matchers are available out of the box.
January 15, 2022112

Circular assertions don’t tend to be a big problem if you are already testing the different parts of your application separately
February 4, 2022116

A test that contains the expected result explicitly written into the assertion would have been far better. It would make the test more readable and
February 4, 2022116

Whenever possible, create separate utility functions for your tests instead of just reusing the application’s code. It’s preferable to have a bit of duplication or hardcoded expected results than to have tests that never fail.
February 4, 2022116

s a whole, they’re called test doubles. Spies record data related to the usage of a function without interfering in its implementation. Stubs record data associated with the usage of a function and change its behavior, either by providing an alternative implementation or return value. Mocks change a function’s behavior, but instead of just recording information about its usage, they have expectations preprogrammed.
February 4, 2022117

Ironically, if we adhere to the most accepted definition of a mock, it’s the only kind of test double that Jest does not include. For the sake of readability and to conform with most people’s vocabulary, throughout this book I’ve used the term mock as a verb that means “to replace with a test double.”
February 4, 2022117

For our first example, let’s consider that, because of accountability purposes, you want to keep logs for whenever someone adds an item to the inventory. To implement this functionality, you’ll use pino , a lightweight library whose documentation you can find at https://getpino.io. Go ahead and install pino as one of your application’s dependencies, as shown in the next listing. Then, create a logger file, which will contain the logger instance you will use.
February 4, 2022117

Verifying calls instead of the logger’s actual behavior simplifies the testing and makes it quicker, but it does not necessarily guarantee that the unit under test logs any information. Verifying the logging itself would depend on an end-to-end test. A test of that kind would have access to the files or streams to which the logger writes. Choosing the kinds of tests to write, as we’ve discussed in the previous chapter, depends on your goal and how much you can spend to achieve it.
February 4, 2022119

Looking at the diff, we can see that the received array still contains the record for the call in the first test. This happens because, just like all other kinds of objects, spies retain their states until you reset them. To reset the state of the logger.logInfo spy, you can use an afterEach to call logger.logInfo.mockClear after each test.
February 4, 2022123

TIP When your tests contain multiple test doubles, instead of manually clearing each one of them, you can reset all doubles at once by using jest.clearAllMocks within a single beforeEach hook. Alternatively, you can add a clearMocks property to your jest.config.js file with the value true to automatically clear all test doubles’ records before each test.
February 4, 2022123

To avoid polluting your test’s output, replace the logger.logInfo function’s implementation with a dummy function. To do that, call the spy’s mockImplementation method, and pass it a dummy function created with jest.fn . TIP You can use jest.fn to quickly create stubs. You can either create a stub that does nothing besides tracking its usage, or you can pass it a function to wrap.
February 4, 2022124



beforeEach(() => inventory.clear());

beforeAll(() => {jest.spyOn(logger,"logInfo").mockImplementation(jest.fn())});
afterEach(() => logger.logInfo.mockClear());

February 4, 2022124

By replacing the logger.logInfo function with your own implementation, you have created a stub . A stub replaces the original implementation of a function. Stubs, just like spies, track the usage of a function so that you can assert on it later. Important In Jest, all stubs are spies, but not all spies are stubs.
February 4, 2022125

asymmetric matcher and expecting memoryUsage to contain any Number . The problem with this approach is that it does not guarantee that the Number in the memoryUsage field comes from process.memoryUsage().rss . To make your tests pass again and ensure that the memoryUsage field comes from the right place, you can provide mockImplementation your own function and assert on the value that you know will be returned, as shown next
February 4, 2022125

Warning The more stubs you use, the less your tests resemble what your program does at run time, and, therefore, the weaker the quality guarantees they create are.
February 4, 2022126

Be thoughtful when using stubs. Use them to make tests run faster and to isolate dependencies or factors you can’t control, but make sure that you also have end-to-end tests to cover cases your stubs won’t.
February 4, 2022126

You can make the beforeEach hook even shorter by using mockReturnValue , as shown next. It allows you to provide a canned response without having to create a function yourself.
February 4, 2022126

Just like spies , stubs will persist their states, including the canned behavior you defined, until they are reset. To reset a stub, you can call its mockReset method. Calling mockReset will cause it to reset all call records and any mocked behavior, but it will remain a spy. To completely restore the original implementation, you should call mockRestore instead
February 4, 2022127

TIP All of Jest’s reset methods have global functions that allow you to clear, reset, or restore all test doubles at once. To help you avoid having to write hooks on each test manually, Jest also allows you to add options to jest .config.js that automatically reset doubles for you. These options are clearMocks , resetMocks , and restoreMocks .
February 4, 2022127

To solve this, you could simply go back to importing logger and accessing properties in it, but in many cases, you will not be able to do that. If you are importing modules that directly export functions or if you simply don’t want to assign your functions to an object, you need a better alternative. Here’s when jest.mock comes into play.
February 4, 2022129

NOTE The jest.fn() function returns empty test doubles. They will record information about their usage but won’t have any canned behavior configured. Their API is the same as the one for test doubles created with spyOn . You can still use methods like mockReturnValue or mockReturnValueOnce .
February 4, 2022130

If you want to avoid calling jest.mock at all, you can add a property called automock to your jest.config.js file and set its value to true . The automock option, when turned on, will cause all imports to resolve to your manual mocks regardless of whether you have called jest.mock before.
February 4, 2022131

choose which one you are going to use, think about what is it that you are trying to “mock.” If you are mocking an object’s property, you should probably use jest.spyOn . If you are mocking an import, you should probably use jest.mock . In case you have to use the same replacement in multiple test files, you should, ideally, use a manual mock placed on the __mocks__ folder.
February 4, 2022132

Pareto principle and has focused on the 20% of tests which generate 80% of the results. Like Louis, we, as engineers, can focus on the 20% of tests that produce 80% of the results. Knowing what to test is valuable, but it’s even more relevant to determine what not to test.
February 4, 2022132

Replace only the parts of your software that are either easy to replace or that can’t be used in your tests at all, such as integration with paid third-party services.
February 12, 2022135

When you mock a particular layer of your application, you prevent your tests from reaching everything underneath it.
February 12, 2022135

The more superficial the layer you choose to mock, the more you choose not to test. The more you want to hide complexity, the earlier you should mock.
February 12, 2022136

In general, too much mocking is when you mock pieces of software that you could easily test without a mock.
February 12, 2022137

Integration tests usually balance cost and benefit better, so, whenever in doubt, choose integration tests.
February 12, 2022138

Code coverage is a metric that indicates how much of your code is executed when you run tests.
February 12, 2022138

By paying attention to your test’s coverage, you were able to detect a blind spot in your test suite and make it more thorough.
February 12, 2022139

That table contains four measurements of coverage: statement coverage, branch coverage, function coverage, and lines coverage
February 18, 2022140

Code coverage does not indicate how good your tests are. It’s perfectly possible to cover 100% of your code and still let bugs slip by.
February 18, 2022141

code coverage on its own is a bad metric. It may show which parts of a program’s code are covered, but it doesn’t indicate which of its possible behaviors are covered, as James O. Coplien explains in his brilliant article “Why Most Un
February 18, 2022142

Testing Is Waste” (https://rbcs-us.com/site/assets/files/1187/why-most-unit-testing-is-waste.pdf).
February 18, 2022142

Instead of using code coverage as a metric on its own, I use it to understand which parts of my program I’ve forgotten to cover and to ensure that my team is always progressing toward more coverage, not less.
February 18, 2022143

Tests in different files will, by default, run in parallel. Running tests in parallel can make tests run faster and, therefore, incentivizes developers to run them more often.
March 2, 2022143

Test doubles: Mocks, stubs, and spies Mocks, stubs, and spies are objects used to modify and replace parts of your application to ease or enable testing. Whereas spies just record data related to the usage of a function, stubs allow you to modify its behavior by providing alternative results or even alternative implementations.
March 3, 2022143

Different kinds of tests can generate a significant amount of overlap. If you have to choose only one type of test, it’s better to choose an integration test
March 3, 2022144

Jest can’t run in a browser
July 31, 2022229

If in your tests you change the input’s content all at once, you will not trigger the multiple validations that would have happened at run time. Because your tests would simulate a situation different from what happens in production, your tests would be unreliable. For example, you wouldn’t catch bugs that happen only as users type.
July 31, 2022230

Write A Comment