posman mocks_使用Mocks测试第三方API

posman mocks

The following tutorial demonstrates how to test the use of an external API using Python mock objects.

以下教程演示了如何使用Python 模拟对象测试外部API的使用。

Integrating with a third-party application is a great way to extend the functionality of your product. However, the added value also comes with obstacles. You do not own the external library, which means that you cannot control the servers that host it, the code that comprises its logic, or the data that gets transferred between it and your app. On top of those issues, users are constantly manipulating the data through their interactions with the library. If you want to enhance the utility of your application with a third-party API, then you need to be confident that the two systems will play nice. You need to test that the two applications interface in predictable ways, and you need your tests to execute in a controlled environment.

与第三方应用程序集成是扩展产品功能的好方法。 但是,附加值也带有障碍。 您不拥有外部库,这意味着您无法控制托管它的服务器,构成其逻辑的代码或在其与应用程序之间传输的数据。 最重要的是,用户通过与图书馆的互动不断地处理数据。 如果您想使用第三方API来增强应用程序的实用性,那么您必须确信这两个系统会很好地配合使用。 您需要以可预测的方式测试两个应用程序的接口,并且需要在受控环境中执行测试。

Mock Objects

At first glance, it might seem like you do not have any control over a third-party application. Many of them do not offer testing servers. You cannot test live data, and even if you could, the tests would return unreliable results as the data was updated through use. Also, you never want your automated tests to connect to an external server. An error on their side could bring a halt to your development if releasing your code depends on whether your tests pass. Luckily, there is a way to test the implementation of a third-party API in a controlled environment without needing to actually connect to an outside data source. The solution is to fake the functionality of the external code using something known as mocks.

乍一看,您似乎对第三方应用程序没有任何控制权。 他们中许多人不提供测试服务器。 您无法测试实时数据,即使可以,测试也会返回不可靠的结果,因为通过使用来更新数据。 同样,您永远也不想让自动化测试连接到外部服务器。 如果发布代码取决于您的测试是否通过,那么他们方面的错误可能会阻止您的开发。 幸运的是,有一种方法可以在受控环境中测试第三方API的实现,而无需实际连接到外部数据源。 解决方案是使用称为模拟的东西来伪造外部代码的功能。

A mock is a fake object that you construct to look and act like real data. You swap it with the actual object and trick the system into thinking that the mock is the real deal. Using a mock reminds me of a classic movie trope where the hero grabs a henchman, puts on his uniform, and steps into a line of marching enemies. Nobody notices the impostor and everybody keeps moving—business as usual.

模拟是伪造的对象,您可以将其构造为看起来和实际数据一样。 您将其与实际对象交换,并诱使系统认为该模拟是真正的交易。 使用模拟让我想起了一部经典的电影剧集,主人公抓住了一个小伙子,穿上他的制服,步入行进的敌人队伍。 没有人注意到冒名顶替者,每个人都在继续前进—照常营业。

Third-party authentication, such as OAuth, is a good candidate for mocking within your application. OAuth requires your application to communicate with an external server, it involves real user data, and your application relies on its success in order to gain access to its APIs. Mocking authentication allows you to test you system as an authorized user without having to go through the actual process of exchanging credentials. In this case you do not want to test whether your system successfully authenticates a user; you want to test how your application’s functions behave after you have been authenticated.

第三方身份验证(例如OAuth)是在您的应用程序中进行模拟的不错的选择。 OAuth要求您的应用程序与外部服务器进行通信,它涉及真实的用户数据,并且您的应用程序依赖于其成功才能访问其API。 模拟身份验证使您可以以授权用户的身份测试系统,而不必经历交换凭据的实际过程。 在这种情况下,您不想测试系统是否成功验证了用户身份。 您想测试通过身份验证后应用程序的功能如何。

NOTE: This tutorial uses Python v3.5.1.

注意:本教程使用Python v3.5.1。

第一步 (First steps)

Begin by setting up a new development environment to hold your project code. Create a new virtual environment and then install the following libraries:

首先建立一个新的开发环境来保存您的项目代码。 创建一个新的虚拟环境,然后安装以下库:

1
1

Here is a quick rundown of each library you are installing, in case you have never encountered them:

如果您从未遇到过它们,这是每个正在安装的库的简要概述:

  • The mock library is used for testing Python code by replacing parts of your system with mock objects. NOTE: The mock library is part of unittest if you are using Python 3.3 or greater. If you are using an older version, please install the backport mock library.
  • The nose library extends the built-in Python unittest module to make testing easier. You can use unittest or other third-party libraries such as pytest to achieve the same results, but I prefer nose’s assertion methods.
  • The requests library greatly simplifies HTTP calls in Python.
  • 模拟库用于通过将系统部分替换为模拟对象来测试Python代码。 注意:如果您使用的是Python 3.3或更高版本,则mock库是unittest一部分。 如果您使用的是旧版本,请安装backport模拟库。
  • 鼻子库扩展了内置的Python unittest模块,使测试更加容易。 您可以使用unittest或其他第三方库(例如pytest)来获得相同的结果,但是我更喜欢鼻子的断言方法。
  • 请求库极大地简化了Python中的HTTP调用。

For this tutorial, you will be communicating with a fake online API that was built for testing – JSON Placeholder. Before you write any tests, you need to know what to expect from the API.

在本教程中,您将与为测试而构建的虚假在线API( JSON占位符)进行通信 。 在编写任何测试之前,您需要知道对API有什么期望。

First, you should expect that the API you are targeting actually returns a response when you send it a request. Confirm this assumption by calling the endpoint with cURL:

首先,您应该期望目标API在发送请求时实际上会返回响应。 通过使用cURL调用端点来确认此假设:

1
1

This call should return a JSON-serialized list of todo items. Pay attention to the structure of the todo data in the response. You should see a list of objects with the keys userId, id, title, and completed. You are now prepared to make your second assumption—you know what to expect the data to look like. The API endpoint is alive and functioning. You proved that by calling it from the command line. Now, write a nose test so that you can confirm the life of the server in the future. Keep it simple. You should only be concerned with whether the server returns an OK response.

该调用应返回待办事项的JSON序列化列表。 注意响应中待办事项数据的结构。 您应该看到包含键userIdidtitlecompleted的对象列表。 现在,您已经准备好进行第二个假设了-您知道期望数据看起来如何。 API端点仍然有效。 您通过从命令行调用来证明了这一点。 现在,编写一个鼻子测试,以便您将来可以确定服务器的寿命。 把事情简单化。 您应该只关心服务器是否返回OK响应。

project/tests/test_todos.py

项目/测试/test_todos.py

1
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
9
9
10
10
11
11

Run the test and watch it pass:

运行测试并观看通过:

1
1
2
2
3
3
4
4
5
5
6
6
7
7

将代码重构为服务 (Refactoring your code into a service)

Chances are good that you will call an external API many times throughout your application. Also, those API calls will likely involve more logic than simply making an HTTP request, such as data processing, error handling, and filtering. You should pull the code out of your test and refactor it into a service function that encapsulates all of that expected logic.

您很有可能会在整个应用程序中多次调用外部API。 而且,这些API调用可能会涉及比简单地发出HTTP请求更多的逻辑,例如数据处理,错误处理和过滤。 您应该从测试中取出代码,然后将其重构为封装了所有预期逻辑的服务函数。

Rewrite your test to reference the service function and to test the new logic.

重写测试以引用服务功能并测试新逻辑。

project/tests/test_todos.py

项目/测试/test_todos.py

1
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
9
9
10
10
11
11
12
12
13
13

Run the test and watch it fail, and then write the minimum amount of code to make it pass:

运行测试并观察它是否失败,然后编写最少的代码以使其通过:

project/services.py

project / services.py

1
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
9
9
10
10
11
11
12
12
13
13
14
14
15
15
16
16
17
17
18
18
19
19
20
20
21
21

project/constants.py

项目/constants.py

1
1

The first test that you wrote expected a response to be returned with an OK status. You refactored your programming logic into a service function that returns the response itself when the request to the server is successful. A None value is returned if the request fails. The test now includes an assertion to confirm that the function does not return None.

您编写的第一个测试期望返回的响应为OK状态。 您将编程逻辑重构为服务函数,当对服务器的请求成功时,该服务函数将返回响应本身。 如果请求失败,则返回None值。 现在,测试包含一个断言,以确认该函数不返回None

Notice how I instructed you to create a constants.py file and then I populated it with a BASE_URL. The service function extends the BASE_URL to create the TODOS_URL, and since all of the API endpoints use the same base, you can continue to create new ones without having to rewrite that bit of code. Putting the BASE_URL in a separate file allows you to edit it in one place, which will come in handy if multiple modules reference that code.

请注意,我是如何指示您创建一个constants.py文件,然后在其中填充BASE_URL 。 服务功能扩展了BASE_URL以创建TODOS_URL ,并且由于所有API端点都使用相同的基础,因此您可以继续创建新的基础,而不必重写那部分代码。 将BASE_URL放在单独的文件中,可以在一处对其进行编辑,如果多个模块引用该代码,它将很方便。

Run the test and watch it pass.

运行测试并观察其通过。

1
1
2
2
3
3
4
4
5
5
6
6
7
7

你的第一个模拟 (Your first mock)

The code is working as expected. You know this because you have a passing test. Unfortunately, you have a problem—your service function is still accessing the external server directly. When you call get_todos(), your code is making a request to the API endpoint and returning a result that depends on that server being live. Here, I will demonstrate how to detach your programming logic from the actual external library by swapping the real request with a fake one that returns the same data.

该代码按预期工作。 您之所以知道这一点,是因为您的考试通过了。 不幸的是,您遇到了问题-您的服务功能仍在直接访问外部服务器。 当您调用get_todos() ,您的代码正在向API端点发出请求,并返回取决于该服务器处于活动状态的结果。 在这里,我将演示如何通过将真实请求与返回相同数据的伪请求交换来将程序逻辑与实际的外部库分离。

project/tests/test_todos.py

项目/测试/test_todos.py

1
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
9
9
10
10
11
11
12
12
13
13
14
14
15
15
16
16
17
17
18
18
19
19
20
20

Notice that I did not change the service function at all. The only part of the code that I edited was the test itself. First, I imported the patch() function from the mock library. Next, I modified the test function with the patch() function as a decorator, passing in a reference to project.services.requests.get. In the function itself, I passed in a parameter mock_get, and then in the body of the test function, I added a line to set mock_get.return_value.ok = True.

请注意,我根本没有更改服务功能。 我编辑的代码的唯一部分是测试本身。 首先,我从mock库导入了patch()函数。 接下来,我使用patch()函数作为装饰器修改了测试函数,并传入了对project.services.requests.get的引用。 在函数本身中,我传入了一个参数mock_get ,然后在测试函数的主体中,添加了一行以设置mock_get.return_value.ok = True

Great. So what actually happens now when the test is run? Before I dive into that, you need to understand something about the way the requests library works. When you call the requests.get() function, it makes an HTTP request behind the scenes and then returns an HTTP response in the form of a Response object. The get() function itself communicates with the external server, which is why you need to target it. Remember the image of the hero swapping places with the enemy while wearing his uniform? You need to dress the mock to look and act like the requests.get() function.

大。 那么,当测试运行时,实际发生了什么呢? 在深入探讨之前,您需要了解有关requests库工作方式的一些知识。 调用requests.get()函数时,它会在后台发出HTTP请求,然后以Response对象的形式返回HTTP响应。 get()函数本身与外部服务器通信,这就是为什么需要将其作为目标。 还记得英雄穿着制服时与敌人交换地点的图像吗? 您需要对模拟进行修饰以使其外观和行为类似于requests.get()函数。

When the test function is run, it finds the module where the requests library is declared, project.services, and it replaces the targeted function, requests.get(), with a mock. The test also tells the mock to behave the way the service function expects it to act. If you look at get_todos(), you see that the success of the function depends on if response.ok: returning True. That is what the line mock_get.return_value.ok = True is doing. When the ok property is called on the mock, it will return True just like the actual object. The get_todos() function will return the response, which is the mock, and the test will pass because the mock is not None.

运行测试函数时,它将找到声明了requests库的模块project.services ,并用模拟替换了目标函数requests.get() 。 该测试还告诉该模拟以服务功能期望其行为的方式运行。 如果查看get_todos() ,则会发现该函数的成功取决于if response.ok:返回True 。 这就是mock_get.return_value.ok = True行的mock_get.return_value.ok = True 。 在模拟程序上调用ok属性时,它将像实际对象一样返回Trueget_todos()函数将返回响应,即模拟,并且测试将通过,因为模拟不是None

Run the test to see it pass.

运行测试以查看是否通过。

1
1

其他修补方式 (Other ways to patch)

Using a decorator is just one of several ways to patch a function with a mock. In the next example, I explicitly patch a function within a block of code, using a context manager. The with statement patches a function used by any code in the code block. When the code block ends, the original function is restored. The with statement and the decorator accomplish the same goal: Both methods patch project.services.request.get.

使用装饰器只是使用模拟程序修补功能的几种方法之一。 在下一个示例中,我使用上下文管理器在代码块中显式修补了函数。 with语句修补代码块中任何代码使用的功能。 代码块结束后,将恢复原始功能。 with语句和装饰器实现相同的目标:两种方法都修补project.services.request.get

project/tests/test_todos.py

项目/测试/test_todos.py

1
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
9
9
10
10
11
11
12
12
13
13
14
14
15
15
16
16
17
17
18
18
19
19
20
20

Run the tests to see that they still pass.

运行测试以查看它们仍然通过。

Another way to patch a function is to use a patcher. Here, I identify the source to patch, and then I explicitly start using the mock. The patching does not stop until I explicitly tell the system to stop using the mock.

修补功能的另一种方法是使用修补程序。 在这里,我确定了要修补的源,然后明确地开始使用该模拟。 在我明确告诉系统停止使用模拟之前,修补不会停止。

project/tests/test_todos.py

项目/测试/test_todos.py

1
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
9
9
10
10
11
11
12
12
13
13
14
14
15
15
16
16
17
17
18
18
19
19
20
20
21
21
22
22
23
23
24
24
25
25
26
26
27
27

Run the tests again to get the same successful result.

再次运行测试以获得相同的成功结果。

Now that you have seen three ways to patch a function with a mock, when should you use each one? The short answer: it is entirely up to you. Each patching method is completely valid. That being said, I have found that specific coding patterns work especially well with the following patching methods.

既然您已经了解了使用模拟程序对函数进行修补的三种方法,那么何时应使用每种方法? 简短的答案:这完全取决于您。 每种修补方法都是完全有效的。 话虽如此,我发现特定的编码模式在以下修补方法中特别有效。

  1. Use a decorator when all of the code in your test function body uses a mock.
  2. Use a context manager when some of the code in your test function uses a mock and other code references the actual function.
  3. Use a patcher when you need to explicitly start and stop mocking a function across multiple tests (e.g. the setUp() and tearDown() functions in a test class).
  1. 当测试函数主体中的所有代码都使用模拟时, 请使用装饰器
  2. 当测试函数中的某些代码使用模拟并且其他代码引用实际函数时, 请使用上下文管理器
  3. 当需要在多个测试中显式启动和停止模拟函数时(例如,测试类中的setUp()tearDown()函数setUp()请使用修补程序

I use each of these methods in this tutorial, and I will highlight each one as I introduce it for the first time.

在本教程中,我将使用每种方法,并且在我第一次介绍每种方法时,将重点介绍每种方法。

模拟完整的服务行为 (Mocking the complete service behavior)

In the previous examples, you implemented a basic mock and tested a simple assertion—whether the get_todos() function returned None. The get_todos() function calls the external API and receives a response. If the call is successful, the function returns a response object, which contains a JSON-serialized list of todos. If the request fails, get_todos() returns None. In the following example, I demonstrate how to mock the entire functionality of get_todos(). At the beginning of this tutorial, the initial call you made to the server using cURL returned a JSON-serialized list of dictionaries, which represented todo items. This example will show you how to mock that data.

在前面的示例中,您实现了一个基本的模拟并测试了一个简单的断言— get_todos()函数是否返回Noneget_todos()函数调用外部API并接收响应。 如果调用成功,该函数将返回一个响应对象,其中包含一个待办事项的JSON序列化列表。 如果请求失败,则get_todos()返回None 。 在下面的示例中,我演示了如何模拟get_todos()的全部功能。 在本教程开始时,您使用cURL对服务器进行的首次调用返回了一个JSON序列化的字典列表,该列表表示待办事项。 本示例将向您展示如何模拟该数据。

Remember how @patch() works: You provide it a path to the function you want to mock. The function is found, patch() creates a Mock object, and the real function is temporarily replaced with the mock. When get_todos() is called by the test, the function uses the mock_get the same way it would use the real get() method. That means that it calls mock_get like a function and expects it to return a response object.

记住@patch()工作原理:您为其提供了要模拟的函数的路径。 找到该函数, patch()创建一个Mock对象,然后将真实函数临时替换为模拟。 当测试调用get_todos() ,该函数以与使用实际get()方法相同的方式使用mock_get 。 这意味着它像函数一样调用了mock_get并期望它返回一个响应对象。

In this case, the response object is a requests library Response object, which has several attributes and methods. You faked one of those properties, ok, in a previous example. The Response object also has a json() function which converts its JSON-serialized string content into a Python datatype (e.g. a list or a dict).

在这种情况下,响应对象是一个requestsResponse对象,它具有多个属性和方法。 在上一个示例中,您伪造了其中一个属性okResponse对象还具有json()函数,该函数将其JSON序列化的字符串内容转换为Python数据类型(例如, listdict )。

project/tests/test_todos.py

项目/测试/test_todos.py

1
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
9
9
10
10
11
11
12
12
13
13
14
14
15
15
16
16
17
17
18
18
19
19
20
20
21
21
22
22
23
23
24
24
25
25
26
26
27
27
28
28
29
29
30
30
31
31
32
32
33
33
34
34
35
35
36
36
37
37
38
38
39
39
40
40
41
41

I mentioned in a previous example that when you ran the get_todos() function that was patched with a mock, the function returned a mock object “response”. You might have noticed a pattern: whenever the return_value is added to a mock, that mock is modified to be run as a function, and by default it returns another mock object. In this example, I made that a little more clear by explicitly declaring the Mock object, mock_get.return_value = Mock(ok=True). The mock_get() mirrors requests.get(), and requests.get() returns a Response whereas mock_get() returns a Mock. The Response object has an ok property, so you added an ok property to the Mock.

我在前面的示例中提到过,当您运行带有模拟补丁的get_todos()函数时,该函数返回了一个模拟对象“ response”。 您可能已经注意到一种模式:每当将return_value添加到模拟中时,该模拟就被修改为作为函数运行,并且默认情况下它将返回另一个模拟对象。 在此示例中,我通过显式声明Mock对象mock_get.return_value = Mock(ok=True)使这一点更加清楚。 mock_get()镜像requests.get() ,而requests.get()返回一个Responsemock_get()返回MockResponse对象具有ok属性,因此您向Mock添加了ok属性。

The Response object also has a json() function, so I added json to the Mock and appended it with a return_value, since it will be called like a function. The json() function returns a list of todo objects. Notice that the test now includes an assertion that checks the value of response.json(). You want to make sure that the get_todos() function returns a list of todos, just like the actual server does. Finally, to round out the testing for get_todos(), I add a test for failure.

Response对象也具有json()函数,因此我将json添加到Mock并附加了return_value ,因为它将像函数一样被调用。 json()函数返回待办事项列表。 注意,测试现在包含一个断言,该断言检查response.json()的值。 您想要确保get_todos()函数返回get_todos()列表,就像实际的服务器一样。 最后,为了完善对get_todos()的测试,我添加了一个失败测试。

Run the tests and watch them pass.

运行测试并观看它们通过。

1
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8

模拟集成功能 (Mocking integrated functions)

The examples I have show you have been fairly straightforward, and in the next example, I will add to the complexity. Imagine a scenario where you create a new service function that calls get_todos() and then filters those results to return only the todo items that have been completed. Do you have to mock the requests.get() again? No, in this case you mock the get_todos() function directly! Remember, when you mock a function, you are replacing the actual object with the mock, and you only have to worry about how the service function interacts with that mock. In the case of get_todos(), you know that it takes no parameters and that it returns a response with a json() function that returns a list of todo objects. You do not care what happens under the hood; you just care that the get_todos() mock returns what you expect the real get_todos() function to return.

我向您展示的示例相当简单,在下一个示例中,我将增加复杂性。 设想一个场景,其中创建了一个新的服务函数,该函数调用get_todos() ,然后过滤这些结果以仅返回已完成的待办事项。 您是否必须再次模拟request.get requests.get() ? 不,在这种情况下,您可以直接模拟get_todos()函数! 请记住,在模拟函数时,您是用模拟替换实际的对象,而您只需要担心服务函数如何与该模拟交互。 对于get_todos() ,您知道它不带任何参数,并且返回一个带有json()函数的响应,该函数返回一个todo对象的列表。 您不在乎引擎盖下会发生什么; 您只关心get_todos()模拟返回的是您期望的实际get_todos()函数返回的内容。

project/tests/test_todos.py

项目/测试/test_todos.py

1
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
9
9
10
10
11
11
12
12
13
13
14
14
15
15
16
16
17
17
18
18
19
19
20
20
21
21
22
22
23
23
24
24
25
25
26
26
27
27
28
28
29
29
30
30
31
31
32
32
33
33
34
34
35
35
36
36
37
37
38
38
39
39
40
40
41
41
42
42
43
43
44
44
45
45
46
46
47
47
48
48
49
49
50
50
51
51
52
52

Notice that now I am patching the test function to find and replace project.services.get_todos with a mock. The mock function should return an object that has a json() function. When called, the json() function should return a list of todo objects. I also add an assertion to confirm that the get_todos() function is actually called. This is useful to establish that when the service function accesses the actual API, the real get_todos() function will execute. Here, I also include a test to verify that if get_todos() returns None, the get_uncompleted_todos() function returns an empty list. Again, I confirm that the get_todos() function is called.

请注意,现在我正在修补测试功能,以查找并用模拟替换project.services.get_todos 。 模拟函数应返回具有json()函数的对象。 调用时, json()函数应返回待办事项列表。 我还添加了一个断言以确认get_todos()函数实际上已被调用。 建立服务功能访问实际API时,将执行实际的get_todos()函数,这很有用。 在这里,我还包括一个测试,以验证如果get_todos()返回None ,则get_uncompleted_todos()函数返回一个空列表。 再次确认是否调用了get_todos()函数。

Write the tests, run them to see that they fail, and then write the code necessary to make them pass.

编写测试,运行它们以查看它们是否失败,然后编写必要的代码以使它们通过。

project/services.py

project / services.py

1
1
2
2
3
3
4
4
5
5
6
6
7
7

The tests now pass.

现在测试通过了。

重构测试以使用类 (Refactoring tests to use classes)

You probably noticed that some of the tests seem to belong together in a group. You have two tests that hit the get_todos() function. Your other two tests focus on get_uncompleted_todos(). Whenever I start to notice trends and similarities between tests, I refactor them into a test class. This refactoring accomplishes several goals:

您可能已经注意到,某些测试似乎属于同一组。 您有两个命中get_todos()函数的测试。 您的其他两个测试集中在get_uncompleted_todos() 。 每当我开始注意到测试之间的趋势和相似性时,就会将它们重构为测试类。 此重构实现了几个目标:

  1. Moving common test functions to a class allows you to more easily test them together as a group. You can tell nose to target a list of functions, but it is easier to target a single class.
  2. Common test functions often require similar steps for creating and destroying data that is used by each test. These steps can be encapsulated in the setup_class() and teardown_class() functions respectively in order to execute code at the appropriate stages.
  3. You can create utility functions on the class to reuse logic that is repeated among test functions. Imagine having to call the same data creation logic in each function individually. What a pain!
  1. 将通用测试功能移到一个类上可以使您更轻松地将它们作为一个组一起测试。 您可以告诉鼻子针对一系列功能,但针对单个类则比较容易。
  2. 通用测试功能通常需要类似的步骤来创建和销毁每个测试使用的数据。 这些步骤可以分别封装在setup_class()teardown_class()函数中,以便在适当的阶段执行代码。
  3. 您可以在类上创建实用程序函数,以重用在测试函数之间重复的逻辑。 想象一下,必须在每个函数中分别调用相同的数据创建逻辑。 真痛苦!

Notice that I use the patcher technique to mock the targeted functions in the test classes. As I mentioned before, this patching method is great for creating a mock that spans over several functions. The code in the teardown_class() method explicitly restores the original code when the tests finish.

注意,我使用修补程序技术来模拟测试类中的目标函数。 如前所述,此修补方法非常适合创建跨越多个函数的模拟。 测试完成后, teardown_class()方法中的代码将显式恢复原始代码。

project/tests/test_todos.py

项目/测试/test_todos.py

1
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
9
9
10
10
11
11
12
12
13
13
14
14
15
15
16
16
17
17
18
18
19
19
20
20
21
21
22
22
23
23
24
24
25
25
26
26
27
27
28
28
29
29
30
30
31
31
32
32
33
33
34
34
35
35
36
36
37
37
38
38
39
39
40
40
41
41
42
42
43
43
44
44
45
45
46
46
47
47
48
48
49
49
50
50
51
51
52
52
53
53
54
54
55
55
56
56
57
57
58
58
59
59
60
60
61
61
62
62
63
63
64
64
65
65
66
66
67
67
68
68
69
69
70
70
71
71
72
72
73
73
74
74
75
75
76
76
77
77
78
78
79
79
80
80
81
81
82
82
83
83
84
84
85
85
86
86
87
87
88
88
89
89
90
90
91
91
92
92
93
93
94
94
95
95
96
96
97
97
98
98
99
99
100
100

Run the tests. Everything should pass because you did not introduce any new logic. You merely moved code around.

运行测试。 一切都会过去,因为您没有引入任何新逻辑。 您只是移动了代码。

1
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
9
9
10
10

测试API数据的更新 (Testing for updates to the API data)

Throughout this tutorial I have been demonstrating how to mock data returned by a third-party API. That mock data is based on an assumption that the real data uses the same data contract as what you are faking. Your first step was making a call to the actual API and taking note of the data that was returned. You can be fairly confident that the structure of the data has not changed in the short time that you have been working through these examples, however, you should not be confident that the data will remain unchanged forever. Any good external library is updated regularly. While developers aim to make new code backwards-compatible, eventually there comes a time where code is deprecated.

在整个教程中,我一直在演示如何模拟第三方API返回的数据。 该模拟数据基于一个假设,即真实数据使用与您伪造的数据相同的数据协定。 第一步是调用实际的API,并记下返回的数据。 您可以完全确定在经历这些示例后的短时间内数据的结构没有改变,但是,您不应该确信数据将永远保持不变。 任何好的外部库都会定期更新。 尽管开发人员旨在使新代码向后兼容,但最终还是不赞成使用代码。

As you can imagine, relying entirely on fake data is dangerous. Since you are testing your code without communicating with the actual server, you can easily become overconfident in the strength of your tests. When the time comes to use your application with real data, everything falls apart. The following strategy should be used to confirm that the data you are expecting from the server matches the data that you are testing. The goal here is to compare the data structure (e.g. the keys in an object) rather than the actual data.

可以想象,完全依靠虚假数据是危险的。 由于您是在不与实际服务器通信的情况下测试代码的,因此您很容易对测试的强度变得过分自信。 当需要将您的应用程序与实际数据一起使用时,一切都崩溃了。 应该使用以下策略来确认您期望从服务器获得的数据与您正在测试的数据相匹配。 这里的目标是比较数据结构(例如,对象中的键)而不是实际数据。

Notice how I am using the context manager patching technique. Here, you need to call the real server and you need to mock it separately.

注意我如何使用上下文管理器修补技术。 在这里,您需要调用真实服务器,并且需要分别对其进行模拟。

project/tests/test_todos.py

项目/测试/test_todos.py

1
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
9
9
10
10
11
11
12
12
13
13
14
14
15
15
16
16
17
17
18
18
19
19
20
20
21
21

Your tests should pass. Your mocked data structure matches the one from the actual API.

您的测试应该通过。 您模拟的数据结构与实际API中的数据结构匹配。

有条件地测试方案 (Conditionally testing scenarios)

Now that you have a test to compare the actual data contracts with the mocked ones, you need to know when to run it. The test that hits the real server should not be automated because a failure does not necessarily mean your code is bad. You might not be able to connect to the real server at the time of your test suite execution for a dozen reasons that are outside of your control. Run this test separately from your automated tests, but also run it fairly frequently. One way to selectively skip tests is to use an environment variable as a toggle. In the example below, all tests run unless the SKIP_REAL environment variable is set to True. When the SKIP_REAL variable is toggled on, any test with the @skipIf(SKIP_REAL) decorator will be skipped.

现在您已经有了一个将实际数据合同与模拟合同进行比较的测试,您需要知道何时运行它。 击中真实服务器的测试不应自动化,因为失败并不一定意味着您的代码是错误的。 在执行测试套件时,您可能无法连接到真实的服务器,这是出于您无法控制的许多原因。 与自动化测试分开运行此测试,但还要相当频繁地运行它。 选择性跳过测试的一种方法是使用环境变量作为切换。 在下面的示例中,除非将SKIP_REAL环境变量设置为True否则所有测试都会运行。 SKIP_REAL变量后,使用@skipIf(SKIP_REAL)装饰器的任何测试都将被跳过。

project/tests/test_todos.py

项目/测试/test_todos.py

1
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
9
9
10
10
11
11
12
12
13
13
14
14
15
15
16
16
17
17
18
18
19
19
20
20
21
21
22
22
23
23
24
24
25
25
26
26
27
27
28
28
29
29

project/constants.py

项目/constants.py

1
1
2
2
3
3
4
4
5
5
6
6
1
1

Run the tests and pay attention to the output. One test was ignored and the console displays the message, “Skipping tests that hit the real API server.” Excellent!

运行测试并注意输出。 一项测试被忽略,控制台显示消息“跳过真正的API服务器的测试。” 优秀的!

1
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
9
9
10
10
11
11

下一步 (Next steps)

翻译自: https://www.pybloggers.com/2016/06/testing-third-party-apis-with-mocks/

posman mocks

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值