javascript测试
Today, we are going to discuss testing in JavaScript and help you start your journey towards understanding and mastering it.
今天,我们将讨论JavaScript测试,并帮助您开始理解和掌握它的旅程。
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.
本文的主要动机是为了清晰地概述JavaScript测试的整个世界,并使之易于理解。 即使您没有测试经验,这篇文章也将被证明是您旅程的完美起点。
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).
测试还可以帮助您减少为特定功能编写的代码量。 测试之后,您的主要目标是编写所需的最少代码以使测试通过。 在编写实际实现之前编写测试的这种编码方式也称为TDD(测试驱动的开发)。
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.
端到端测试从头到尾验证复杂的场景,并且通常需要使用外部资源,例如数据库或Web服务器。 想象一下,您的应用程序具有包含几个步骤的注册流程,并且您想要测试整个流程,这就是端到端测试的作用。
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.
假设您有一个函数可以计算某个阶乘数的结果。 然后,可以使用Expect()函数和一个简单的匹配器来测试该函数,该匹配器检查函数的结果是否与期望值匹配。
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.
这是Jest测试框架中生命周期方法的示例。
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.
依赖注入是一个对象提供另一个对象的依赖关系的概念。 您只需要做的就是让另一个对象为您提供所需的实例,而不是在创建新对象时使用new关键字。
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.
当您需要更改某些对象的实现时,例如,当您为特定测试模拟它时,此概念会有所帮助。 许多现代框架(例如Angular和Nest.js)已经内置了依赖项注入,但是最好知道它在基本级别上如何工作。
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.
单一责任原则,也称为SRP,是SOLID原则之一,它定义了功能应具有单一目的。 这使得测试每个功能是否正确发挥作用变得容易得多。
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.
得墨meter耳定律(也称为“最少知识原理”)规定,特定单位对与其协调的其他单位的了解有限。 您的代码越依赖于与之交互的对象的内部细节,在为它们编写测试时就会遇到更多的困难。
More information about the Law of Demeter can be found here.
有关Demeter定律的更多信息,可以在这里找到。
各种测试工具概述 (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.
现在,您已概述了测试领域的基本概念以及何时需要使用它们,让我们继续以简短的摘要介绍一下当今可用的各种Javascript测试工具。
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.
Jest是Facebook创建的一个开放源代码测试框架,专注于简单性。 Jest通过开箱即用并需要零配置来使编写JavaScript测试更快,更轻松。 Jest还可以并行运行测试,从而提供更流畅,更快的测试运行。
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.
赛普拉斯用于处理在现代Javascript堆栈上运行的复杂UI。 通过使用它们的框架和声明库,可以轻松验证UI中的状态。 然后,赛普拉斯将自动等待您的应用程序达到该状态,然后再继续。
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.
与Jest和Mocha相比,赛普拉斯是一种更新,更现代的工具,对于一般的初学者和端到端测试而言,赛普拉斯是一个很好的起点。
笑话简介 (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测试框架,因为它是目前最流行的框架。 但是大多数概念都适用于所有测试框架,并且无论您使用哪种技术,它都可能有用。
Jest is an open-source project maintained by Facebook and is especially well suited for Unit and Integration testing. Its strengths are:
Jest是Facebook维护的一个开源项目,特别适合单元测试和集成测试。 它的优势是:
- 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.
注意,这会将Jest作为开发依赖项安装为当前项目中package.json文件的一部分。 您也可以选择在全球范围内安装它。
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.
您还可以将此行添加到package.json中,以使用test命令运行测试。
{ "scripts": { "test": "jest" } }
您的第一个测试 (Your first test)
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.
现在我们已经安装了Jest,现在终于可以编写我们的第一个测试了。 但是在执行此操作之前,我们将首先编写一些可以测试的基本代码。
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:
在终端中运行yarn test命令,将为您提供以下输出:
匹配器 (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).
它们最常用于将Expect()函数的结果与作为参数传递给匹配器的值进行比较(这也是我们上面所做的)。
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 nulltoBeNull —检查值是否为null
- toBeDefined — checks if the value is defined toBeDefined-检查值是否已定义
- toBeUndefined — checks if the value is undefinedtoBeUndefined —检查值是否未定义
- 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 valuetoContain —检查Expect()的结果是否包含值
- toHaveProperty — checks if an object has a property, and optionally checks its valuetoHaveProperty —检查对象是否具有属性,并可选地检查其值
- toBeInstanceOf — checks if an object is an instance of a classtoBeInstanceOf —检查对象是否是类的实例
These matchers can also be negated using the not statement:
也可以使用not语句否定这些匹配器:
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.
您还可以使用Jest社区维护的其他匹配器。
设置和拆卸 (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()和afterAll()帮助程序函数,这些函数将在测试开始之前和完成之后执行。
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()和afterEach()函数。
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.
在Jest中测试诺言很简单。 只需返回承诺,Jest将等待承诺解决。 如果承诺失败,则测试也会自动失败。
// 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.
您还可以使用catch()函数捕获被拒绝的承诺。
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.
默认情况下,Jest测试在执行结束时会完成,这意味着测试将在调用回调之前完成。 可以通过将一个名为done的参数传递给测试函数来解决此问题。 Jest将等待直到完成的回调被调用,然后再完成测试。
// 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.
如果从不调用done() ,则测试将失败,并显示超时错误。
模拟 (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.
使用Mock函数的常见方法之一是将其作为参数传递给要测试的函数。 这使您可以运行测试,而无需导入将在实际应用程序中传递的实际依赖关系和对象。
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():
您可以使用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.
在这里,我创建了lodash.chunk函数的模拟并测试它是否被调用以及参数是否正确。
模拟模块: (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来自动设置整个模块的导出,而不是手动模拟模块。
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.
如您所见,我现在可以将lodash库的所有函数称为模拟对象。
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.
该策略的唯一缺点是很难访问该模块的原始实现。 对于这些用例,可以改用spyOn函数。
间谍包: (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.
您也可以监视软件包而无需为其创建模拟。 这是通过Jest提供的spyOn()函数完成的。
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.
在掌握JavaScript测试方法的过程中,您肯定还应该考虑一些其他概念。 但是,我强烈建议您先学习基础知识,然后在您的应用程序中将其付诸实践。
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 correctCD(持续部署)—是一种软件发布过程,使用自动测试来验证对代码库的更改是否正确
- Automated dependency updates自动化的依赖项更新
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.
一路走到最后! 我希望本文能帮助您了解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.
如果您发现此功能有用,请考虑推荐并与其他开发人员共享。 如果您有任何疑问或反馈,请使用我的联系表或通过Twitter与我联系。
Originally published at https://gabrieltanner.org.
翻译自: https://medium.com/dailyjs/an-introduction-to-testing-in-javascript-26ed0a6da6f4
javascript测试