# javascript测试_javascript测试简介

javascript测试

Today, we are going to discuss testing in JavaScript and help you start your journey towards understanding and mastering it.

Testing is one of the most important topics in software development, but a lot of developers still shy away from it. This article is here to change that.

The primary motivation behind this article is to give a clear overview of the entire world of JavaScript testing and making it simple to understand. Even if you have no prior experience in testing, this article will prove to be the perfect start for your journey.

So, without wasting any further time, let’s get started.

# 为什么测试很重要 (Why testing is important)

Before diving into the various types and concepts of software testing, you should first have a clear understanding of why you should actually care about automated testing in the first place.

# 建立对代码的信心： (Building Confidence In Your Code:)

To know that your code is working as planned, it needs to be tested in some kind. Manual test work for most small applications but don’t provide the security and confidence level you get using automated tests.

Automated tests make it easy to test almost every possible situation and allow you to run them whenever you are making a change to your code.

Identifying every success and failure case and writing tests for them will ensure that you are confident with the code you are deploying for production.

# 编写最小代码： (Writing Minimal Code:)

Testing also helps you to reduce the amount of code you are writing for a particular feature. After testing, your main goal is to write the minimal required code to make the tests pass. This coding style where you write tests before you write the actual implementation is also known as TDD (Test-driven development).

After successfully testing, you can focus on writing clean implementations with as minimal code as possible.

# 摆脱回归错误： (Getting Rid Of Regression Bugs:)

Do you know the feeling when you have just finished a new feature of your application and want to release it to production and all of a sudden, an old feature isn’t working anymore? You are absolutely clueless about why this is happening and will probably waste a lot of time searching for the issue.

This situation would have never occurred if you had tested your old features. You could have frequently run those tests to check if your application is still working as expected. The tests would also give you a better idea of what exactly isn’t working anymore because the appropriate test cases would fail.

# 测试类型 (Types of tests)

There are a few different types of tests, and it is essential to know how they differ from each other. Most applications will require you to write multiple kinds of tests to get the best result possible.

# 单元测试： (Unit tests:)

The purpose of a unit test is to validate the functionality of a relatively small piece of software, independently from other parts. Unit tests are narrow in scope, which allows us to cover all cases to ensure that every single part works correctly.

They are small and highly focused tests that can efficiently be executed on your local machine because of their fast execution time. You are going to have hundreds, if not thousands of these tests and run them on a regular basis while developing.

The only downside to these kinds of tests is that they are not executed on real devices and therefore have lower fidelity than the other types of tests.

# 集成测试： (Integration tests:)

Integration tests demonstrate that the different parts of your application work together in a real-life production environment. They verify that two separate modules or components are working together in the way they should.

These tests are of medium size and have a much higher execution time then Unit tests. They aren’t executed as often but are still vital for checking the health status of your applications. Their fidelity is also a lot higher because they run on real devices and verify the actual interaction between various components of your application.

# 端到端测试： (End-to-End tests:)

End-to-End tests validate complex scenarios from end to end, and usually require external resources, like databases or web servers, to be present. Imagine you have an application with a sign-up flow comprising of several steps, and you want to test the entire flow, that’s where End-to-End tests come into play.

E2E tests will also run on real devices just like integration tests and therefore, will again be quite slow in their execution.

E2E测试也将像集成测试一样在真实设备上运行，因此，它们的执行速度仍然很慢。

The only downside to these kinds of tests is that debugging them and finding out what went wrong if a particular test fails becomes very hard because of their vast scope.

# 概念 (Concepts)

Before starting to write tests for your code, you first need to be familiar with the most crucial testing concepts and when you need to use them. These concepts will influence the architecture of your application and how you write code in general but more on that in a later section.

# 匹配器： (Matchers:)

Matchers let you validate the results and values of your tests in different ways and are used to make sure that the results of the test match your expectations.

Imagine you have a function that calculates the result of a certain factorial number. Testing the function can then be done using the expect() function and a simple matcher that checks if the result of the function matches the expected value.

test('factorial of 2', () => { expect(factorial(2)).toBe(2); });

The expect() function checks if the result meets the conditions defined by the matcher. We will make use of different matchers in the Jest testing framework at a later point in this guide.

Expect()函数检查结果是否满足匹配器定义的条件。 在本指南的后面，我们将在Jest测试框架中使用不同的匹配器。

# 模拟： (Mocking:)

An object under a test might have dependencies on other objects or services. To isolate the behavior of an object, you want to replace the other objects it interacts with by mocks that simulate the behavior of the real objects.

Mocks help your tests to avoid test unreliability (flakiness) and improve the speed of your tests. They are also useful if the real objects are impractical to incorporate into tests.

In short, mocking is creating objects or services that simulate the behavior of real objects (A database, for example).

# 生命周期： (Lifecycle:)

When testing, you often execute multiple tests after each other and have some setup work that needs to happen before the tests run. Most frameworks provide helper functions to handle these scenarios.

Here is an example of lifecycle methods in the Jest testing framework.

beforeEach(() => { // Initialize objects });afterEach(() => { // Tear down objects });

# 可测试的架构 (Testable architecture)

Before starting to write tests for your code, you first need to make sure that your application’s architecture is testable. If it is not, you need to understand why not and what you can do about it.

Untestable architecture is probably the most common reason why many people find testing tedious and difficult. If your code is not structured properly, you are definitely going to find it difficult to write tests for it.

Let’s explore some important concepts you should know when talking about testable architecture.

# 依赖注入： (Dependency injection:)

Dependency injection is a concept where an object supplies the dependencies of another object. Instead of using the new keyword whenever creating a new object, all you need to do is ask the other object to give you the instance you want.

This concept helps when you need to change the implementation of some object, e.g. when you mock it for a particular test. Many modern frameworks like Angular and Nest.js have dependency injection already build in, but it is still good to know how it functions at a base level.

For more information on dependency injection, you can visit the following article.

# SRP(单一责任原则)： (SRP (Single responsibility principle):)

The single responsibility principle, also known as SRP, is one of the SOLID principles and defines that a function should have a single purpose. This makes it far easier to test that each function does its part correctly.

If your function or service is performing more than one responsibility, then it is time to identify those responsibilities and separate them into individual functions.

# 避免副作用： (Avoid side effects:)

Your functions depend on external variables and services, and you have to set up that variable or service before testing your function. You’ll also have to trust that any other code being run isn’t altering that same variables and states.

That is why you should avoid writing functions that alter any external state (like writing to a file or saving values to a database). This prevents side effects and allows you to test your code with confidence.

# 得墨meter耳定律： (Law of Demeter:)

The Law of Demeter, which is also known as the “principle of least knowledge” states that a specific unit should have limited knowledge of the other units it coordinates with. The more your code depends on the internal details of the objects it interacts with, the more difficulty you will have when writing tests for them.

# 各种测试工具概述 (Overview of the different testing tools)

Now that you have an overview of the essential concepts in the testing world and when you need to use them, let’s continue by looking at a short summary of the different Javascript testing tools that are available today.

Note: I will not cover every tool there is but instead look at the most important once to give you a quick overview of there benefits and weaknesses.

Jest is an open-source testing framework created by Facebook with a focus on simplicity. Jest makes it faster and easier to write JavaScript tests by having everything included out of the box and needing zero configuration. Jest also runs your tests in parallel, providing a smoother, faster test run.

Mocha is a flexible JavaScript testing libraries available and aims to make asynchronous testing simple and fun. It provides developers with a basic test framework and gives them the option to choose which assertion, mocking, and spy libraries they want to use.

Mocha是一个灵活JavaScript测试库，旨在使异步测试变得简单而有趣 它为开发人员提供了一个基本的测试框架，并为他们提供了选择要使用的断言，模拟和间谍库的选项。

It requires some additional setup and configuration but gives you complete control of your testing framework in return.

Cypress is an all in one testing tool that is focused on making End-to-End testing easy and modern. Their tests are executed in the browser themself which gives them a better execution time and no network lag.

Cypress is used to deal with complex UI’s running on modern Javascript stacks. By using their framework and assertion library, it becomes easy to validate states in the UI. Cypress will then automatically wait for your application to reach this state before moving on.

Cypress is a newer and more modern tool than Jest and Mocha and is an excellent start for beginners and End-to-End testing in general.

# 笑话简介 (Introduction to Jest)

As already mentioned above, this guide will focus on the Jest testing framework because it is the most popular framework out there. But most of the concepts apply to all testing frameworks and can be useful no matter which technology you are using.

Jest is an open-source project maintained by Facebook and is especially well suited for Unit and Integration testing. Its strengths are:

• It is simplistic and fast

它既简单又快速
• It provides everything out of the box and thereby doesn’t require and configuration (though you can change the configuration if you so choose)

它提供了开箱即用的所有内容，因此不需要配置(尽管您可以选择更改配置)
• It can perform snapshot testing

它可以执行快照测试

Now we will explore some practical examples so you can put your knowledge into practice.

# 安装 (Installation)

Jest can be installed using either npm or yarn:

Jest可以使用npm或yarn进行安装：

yarn add --dev jest # or npm install --save-dev jest

Notice that this will install Jest as a dev dependency as part of your package.json file in the current project. You can alternatively install it globally if you so choose.

yarn global add jest # or npm install jest -g

You can also add this line to your package.json to run your test using the test command.

{ "scripts": { "test": "jest" } }

Now that we have installed Jest it is finally time to write our first test. But before we do that, we will write some basic code that we can test in the first place.

For that, we will create two files so we can get going.

touch maths.js touch maths.spec.js

We will use the following function for calculating a factorial number to write our first test.

function factorialize(num) {
if (num < 0) return -1;
else if (num == 0) return 1;
else {
return num * factorialize(num - 1);
}
}

module.exports = { factorialize }

Here are some very basic test cases for this small function.

const { factorialize } = require("./maths");

test("factorial of 3", () => {
expect(factorialize(3)).toBe(6);
});

test("factorial of 5", () => {
expect(factorialize(5)).toBe(120);
});

Running the yarn test command in your terminal should give you the following output:

# 匹配器 (Matchers)

As already said above matchers let you validate the results and values of your tests in different ways.

They are most commonly used to compare the result of the expect() function to the value passed as an argument to the matcher (That is also what we did above).

Here is a list of the most common matchers:

• toBe — compares for strict equality (e.g. ===)

toBe —比较严格相等(例如===)
• toEqual — compares the values of two variables/objects

toEqual —比较两个变量/对象的值
• toBeNull — checks if the value is null

toBeNull —检查值是否为null
• toBeDefined — checks if the value is defined

toBeDefined-检查值是否已定义
• toBeUndefined — checks if the value is undefined

toBeUndefined —检查值是否未定义
• toBeTruthy — checks if the value is true (similar to an if statement)

toBeTruthy-检查值是否为真(类似于if语句)
• toBeFalsy — checks if the value is false(similar to an if statement)

toBeFalsy —检查值是否为false(类似于if语句)
• toBeGreaterThan — checks if the result of the expect() function is greater than the argument

toBeGreaterThan —检查Expect()函数的结果是否大于参数
• toContain — checks if the result of expect() contains a value

toContain —检查Expect()的结果是否包含值
• toHaveProperty — checks if an object has a property, and optionally checks its value

toHaveProperty —检查对象是否具有属性，并可选地检查其值
• toBeInstanceOf — checks if an object is an instance of a class

toBeInstanceOf —检查对象是否是类的实例

These matchers can also be negated using the not statement:

test("factorial of 3 is not 5", () => {     expect(factorialize(3)).not.toBe(5); });

You can also use additional matchers that are maintained by the Jest community.

# 设置和拆卸 (Setup and Teardown)

Often when writing tests, you will have to do some kind of setup like initializing variables before tests run and some sort of action after they have finished.

Jest provides two different ways you can do that.

Jest提供了两种不同的方式来实现这一目标。

# 一次性设置： (One-Time Setup:)

In some cases, you only need to do the setup once, at the beginning of your test file. In that scenario, you can use the beforeAll() and afterAll() helper functions that will execute before the tests start and after all have finished.

beforeAll(() => {
return initializeDatabase();
});

afterAll(() => {
return clearDatabase();
});

test('query from database', () => {
expect(database.getObject('Florida')).toBeTruthy();
});

# 为每个测试重复设置：(Repeating setup for each test:)

If you have a setup process that needs to run before each test than you should use the beforeEach() and afterEach() functions.

beforeEach(() => {
initializeDatabase();
});

afterEach(() => {
clearDatabase();
});

test('query from database', () => {
expect(database.getObject('Florida')).toBeTruthy();
});

Note: There will be scenarios where you will use both of these setup processes together to get the best results.

# 分组测试 (Grouping tests)

You can also group related tests together so you can isolate the setup and teardown functions. Grouping tests will also help you get a better overview of your different test cases.

describe('testing factorial function', () => {
beforeAll(() => {
//do something
})
afterAll(() => {
//do something
})

test("factorial of 3", () => {
expect(factorialize(3)).toBe(6);
});

test("factorial of 5", () => {
expect(factorialize(5)).toBe(120);
});

test("factorial of 3 is not 5", () => {
expect(factorialize(3)).not.toBe(5);
});
})

# 测试异步功能(Testing async functions)

It is common for Javascript code to run asynchronously using either promises or callbacks. The problem with testing asynchronous code is knowing when the code that you are testing is actually complete. Jest has several ways to handle this.

Java代码通常使用promise或callbacks异步运行。 测试异步代码的问题是知道您要测试的代码何时真正完成。 笑话有几种处理方法。

# 承诺： (Promises:)

Testing promises is straight forward in Jest. Just return the promise and Jest will wait for the promise to resolve. If the promise fails, the test will automatically fail as well.

// string.js
const reverseString = str => {
return new Promise((resolve, reject) => {
if (!str) {
reject("Empty string");
return;
}
resolve(str.split("").reverse().join(""));
});
};
module.exports = reverseString;

// string.spec.js
const reverseString = require("./string");

test(reverseString 'String' to equal 'gnirtS', () => {
return reverseString("String").then(str => {
expect(str).toBe("gnirtS");
});
});

You can also catch rejected promises using the catch() function.

test(reverseString '' to reject promise, () => {
return reverseString("String").catch(error => {
expect(e).toMatch("Empty string");
});
});

# 异步等待：(Async await:)

Alternatively, we can use async and await for testing promises.

const reverseString = require("./string");

test(reverseString 'String' to equal 'gnirtS' using await, async () => {
const str = await reverseString("String")
expect(str).toBe("gnirtS");
});

Note: You need to make your testing function async to use async and await.

# 回调： (Callbacks:)

By default Jest tests complete once they reach the end of their execution which means that the test will be completed before the callback is called. This can be fixed by passing a single argument named done to your test function. Jest will wait until the done callback is called before finishing the test.

// string.js
function reverseStringCallback(str, callback) {
callback(str.split("").reverse().join(""))
}

module.exports = {reverseStringCallback};

// string.spec.js
const {reverseStringCallback} = require("./string");

test(reverseStringCallback 'string' to equal 'gnirts', (done) => {
reverseStringCallback('string', (str) => {
expect(str).toBe('gnirts')
done()
})
})

If done() is never called, the test will fail with a timeout error.

# 模拟 (Mocking)

Mocking is creating objects or services that simulate the behavior of real objects and plays a vital part in testing. The goal for mocking an object or function is to replace something we don’t control like an external service with something we do, that is why it’s essential that what we replace it with something that has all the features we need.

Using mocks also helps you to inspect information about your code e.g. if a function has already be called and which parameters were used.

# 将Mocks传递给函数： (Passing Mocks to functions:)

One of the common ways to use the Mock function is by passing it as an argument to the function you are testing. This allows you to run your tests without importing the real dependencies and objects you would pass in your real application.

const multiplyNumbers = (a, b, callback) => {
callback(a * b);
};

test("calls callback with arguments added", () => {
const mockCallback = jest.fn();
multiplyNumbers(1, 2, mockCallback);
expect(mockCallback).toHaveBeenCalledWith(2);
});

This strategy is great but requires that your code supports dependency injection. If that is not the case you will need to mock already existing modules or functions instead.

# 模拟单个功能： (Mocking a single function:)

You can mock a single function using Jest.fn():

const lodash = require('lodash')

lodash.chunk = jest.fn(() => 'test')
test(Test lodash chunk function, () => {
const result = lodash.chunk(['a', 'b', 'c', 'd'], 2)
expect(result).toBe('test')
expect(lodash.chunk).toHaveBeenCalled()
expect(lodash.chunk).toHaveBeenCalledWith(['a', 'b', 'c', 'd'], 2)
})

Here I create a mock of the lodash.chunk function and test if it gets called and if the parameters are right.

# 模拟模块： (Mocking Modules:)

Mocking single functions works well if you only use one or two functions of a package or library but can get very cluttered when you need more functions of a module. Here we use jest.mock to automatically set the exports of an entire module instead of mocking the modules manually.

jest.mock('lodash');

test(Test lodash chunk function, () => {
const result = lodash.chunk(['a', 'b', 'c', 'd'], 2)
expect(lodash.chunk).toHaveBeenCalled()
expect(lodash.chunk).toHaveBeenCalledWith(['a', 'b', 'c', 'd'], 2)

const concatResult = lodash.concat(2, [3], [[4]]);
expect(lodash.concat).toHaveBeenCalled()
expect(lodash.concat).toHaveBeenCalledWith(2, [3], [[4]])
})

As you can see I can now call all function of the lodash library as mock objects.

The only disadvantage of this strategy is that it is difficult to access the original implementation of the module. For those use cases, you can use the spyOn function instead.

# 间谍包： (Spying packages:)

You can also spy on a package without creating a mock for it. This is done using the spyOn() function provided by Jest.

const lodash = require('lodash')

test(Test lodash chunk function, () => {
const spy = jest.spyOn(lodash, 'chunk')
const result = lodash.chunk(['a', 'b', 'c', 'd'], 2)

expect(lodash.chunk).toHaveBeenCalled()
expect(lodash.chunk).toHaveBeenCalledWith(['a', 'b', 'c', 'd'], 2)
})

# 您应该注意的重要事项(Important things you should look at)

There are a few more concepts you should definitely look at on your way to mastering testing in JavaScript. But I would greatly recommend learning the basics first and putting them into practice in your applications.

• Snapshot testing — Is used for testing the UI of your application

快照测试-用于测试应用程序的UI

• CI (Continous Integration) — The practice of automating the integration of code changes from multiple contributors into a single software project

CI(持续集成)—将代码更改从多个贡献者自动集成到单个软件项目中的实践
• CD (Continous Deployment) — Is a software release process that uses automated testing to validate if changes to a codebase are correct

CD(持续部署)—是一种软件发布过程，使用自动测试来验证对代码库的更改是否正确

自动化的依赖项更新

It is vital to practice testing in your own applications. If you do it long enough, then you will master the art of testing and make your applications more stable and secure in the process.

# 资料来源： (Sources:)

Here is a list of the sources I used for this article:

# 结论(Conclusion)

You made it all the way until the end! I hope that this article helped you understand the basics of automated testing in JavaScript.

If you have found this useful, please consider recommending and sharing it with other fellow developers. If you have any questions or feedback, let me know using my contact form or contact me on Twitter.

Originally published at https://gabrieltanner.org.

javascript测试

12-06 2667
08-14 134
03-13 1095
03-31 82
07-30 178
07-08 61
05-12 296
11-24 260
07-31 9717
12-19 3万+
04-12 9045
11-23 97
03-03 350
01-25 1万+
04-02 316
09-30 3447
09-27 4741
12-13 800
03-24 316

• 非常没帮助
• 没帮助
• 一般
• 有帮助
• 非常有帮助