英文:
Wiremock stateful behavior doesn't work as expected with multiple scenarios
问题
我有一个测试辅助函数 createUser
,它向后端API发出HTTP请求来创建用户。后端API调用Auth0客户端的方法来创建用户。我有两个用于Auth0客户端URL路径 /api/v2/users
的响应。
我正在测试一个服务,该服务有两种类型的用户 - 账户的所有者和被邀请的用户。在所有测试之前,我调用了两次辅助函数:
const owner = await createUser('owner@email.com');
const invitee = await createUser('invitee@email.com');
我期望 owner
具有 owner@email.com
作为电子邮件,invitee
具有 invitee@email.com
作为电子邮件。
为了返回两个不同的响应,我使用了Wiremock的场景。目前,我的响应如下所示:
用户 #1
{
"scenarioName": "user-scenario",
"requiredScenarioState": "Started",
"newScenarioState": "owner-user",
"request": {
"method": "POST",
"urlPattern": "/api/v2/users"
},
"response": {
"status": 201,
"headers": {
"Content-Type": "application/json"
},
"jsonBody": {
"email": "owner@email.com"
}
}
}
用户 #2
{
"scenarioName": "user-scenario",
"requiredScenarioState": "owner-user",
"request": {
"method": "POST",
"urlPattern": "/api/v2/users"
},
"response": {
"status": 201,
"headers": {
"Content-Type": "application/json"
},
"jsonBody": {
"email": "invitee@email.com"
}
}
}
当我运行测试时,Wiremock返回了两次 createUser
调用的第二个响应。第二个响应是在第一个响应之后创建的,并且Wiremock优先返回最近创建的响应。我决定在第一个响应上设置 priority: 1
以强制Wiremock返回它,从而改变场景状态。这只在第一次运行测试时有效,对于后续运行则无效。有时,如果我删除Wiremock Docker镜像并从头开始启动容器,它会按预期返回响应,但然后继续返回两次调用的第二个响应。
我做错了什么,如果设置场景不是问题,可能导致这种不一致性的可能原因是什么?
英文:
I have a test helper function createUser
that makes an HTTP request to the backend API to create a user. The backend API calls a method on Auth0 client to create a user. The responses I have are set for Auth0 client URL path /api/v2/users
.
I am testing a service that has two types of users - an owner of the account and the invitee user. I am calling a helper twice before all the tests:
const owner = await createUser('owner@email.com');
const invitee = await createUser('invitee@email.com');
And I expect owner
to have owner@email.com
as an email and an invitee@email.com
email for invitee
.
In order to return two different responses I used Wiremock scenarios. And currently I have the responses like this:
User #1
{
"scenarioName": "user-scenario",
"requiredScenarioState": "Started",
"newScenarioState": "owner-user",
"request": {
"method": "POST",
"urlPattern": "/api/v2/users"
},
"response": {
"status": 201,
"headers": {
"Content-Type": "application/json"
},
"jsonBody": {
"email": "owner@email.com"
}
}
}
User #2
{
"scenarioName": "user-scenario",
"requiredScenarioState": "owner-user",
"request": {
"method": "POST",
"urlPattern": "/api/v2/users"
},
"response": {
"status": 201,
"headers": {
"Content-Type": "application/json"
},
"jsonBody": {
"email": "invitee@email.com"
}
}
}
When I run the tests Wiremock returns the second response for both createUser
calls. The second response was created after the first and Wiremock prioritizes the most recently created response. I decided to set the priority: 1
on the first response to force Wiremock to return it and therefore change the scenario state. That worked only on the first test run but not for the subsequent ones.
Sometimes if I delete the Wiremock Docker image and start the container from scratch it returns the responses as expected but then continues returning the second response for both calls.
What am I doing wrong and if it's not how I set up the scenarios what could be the possible reasons for such inconsistency?
答案1
得分: 3
以下是您要翻译的内容:
"Multiple ways to skin this cat - third (response templating) is probably best.
Understanding Scenarios
The first request moves the scenario into state owner-user
, and after that all requests will return user #2 for the lifetime of the WireMock instance - in your case, the docker container - unless it is reset.
You can reset it as so: PUT /__admin/scenarios/user-scenario/Started
.
The state of a scenario is held in memory, so restarting the container should also reset the state to Started
.
See WireMock | Stateful Behaviour | Resetting a single scenario
Using better matching to avoid scenarios
You may not need to use scenarios at all. You can use the request body of the POST to decide which stub to call, based on the email address you are sending.
Assuming your request payload looks like this:
{
"email": "owner@email.com"
}
You can match on the request body as so:
User #1
{
"request": {
"method": "POST",
"urlPattern": "/api/v2/users",
"bodyPatterns": [
{
"matchesJsonPath": {
"expression": "$.email",
"equalTo": "owner@email.com",
}
}
]
},
"response": {
"status": 201,
"headers": {
"Content-Type": "application/json"
},
"jsonBody": {
"email": "owner@email.com"
}
}
}
User #2
{
"request": {
"method": "POST",
"urlPattern": "/api/v2/users",
"bodyPatterns": [
{
"matchesJsonPath": {
"expression": "$.email",
"equalTo": "invitee@email.com",
}
}
]
},
"response": {
"status": 201,
"headers": {
"Content-Type": "application/json"
},
"jsonBody": {
"email": "invitee@email.com"
}
}
}
See WireMock | Request Matching | JSON Path for details.
Using response templating to reduce number of stubs
You can use values passed to WireMock in the request in the response using response templating as so:
{
"request": {
"method": "POST",
"urlPattern": "/api/v2/users"
},
"response": {
"status": 201,
"headers": {
"Content-Type": "application/json"
},
"transformers": ["response-template"],
"jsonBody": {
"email": "{{jsonPath request.body '$.email'}}"
}
}
}
See WireMock | Response Templating | JSONPath helper for details.
There are also a variety of helpers for generating random data in various formats should you need each request to return some different value (e.g. a UUID) - see WireMock | Response Templating | Random value helper for details."
英文:
Multiple ways to skin this cat - third (response templating) is probably best.
Understanding Scenarios
The first request moves the scenario into state owner-user
, and after that all requests will return user #2 for the lifetime of the WireMock instance - in your case, the docker container - unless it is reset.
You can reset it as so: PUT /__admin/scenarios/user-scenario/Started
.
The state of a scenario is held in memory, so restarting the container should also reset the state to Started
.
See WireMock | Stateful Behaviour | Resetting a single scenario
Using better matching to avoid scenarios
You may not need to use scenarios at all. You can use the request body of the POST to decide which stub to call, based on the email address you are sending.
Assuming your request payload looks like this:
{
"email": "owner@email.com"
}
You can match on the request body as so:
User #1
{
"request": {
"method": "POST",
"urlPattern": "/api/v2/users",
"bodyPatterns": [
{
"matchesJsonPath": {
"expression": "$.email",
"equalTo": "owner@email.com",
}
}
]
},
"response": {
"status": 201,
"headers": {
"Content-Type": "application/json"
},
"jsonBody": {
"email": "owner@email.com"
}
}
}
User #2
{
"request": {
"method": "POST",
"urlPattern": "/api/v2/users",
"bodyPatterns": [
{
"matchesJsonPath": {
"expression": "$.email",
"equalTo": "invitee@email.com",
}
}
]
},
"response": {
"status": 201,
"headers": {
"Content-Type": "application/json"
},
"jsonBody": {
"email": "invitee@email.com"
}
}
}
See WireMock | Request Matching | JSON Path for details.
Using response templating to reduce number of stubs
You can use values passed to WireMock in the request in the response using response templating as so:
{
"request": {
"method": "POST",
"urlPattern": "/api/v2/users"
},
"response": {
"status": 201,
"headers": {
"Content-Type": "application/json"
},
"transformers": ["response-template"],
"jsonBody": {
"email": "{{jsonPath request.body '$.email'}}"
}
}
}
See WireMock | Response Templating | JSONPath helper for details.
There are also a variety of helpers for generating random data in various formats should you need each request to return some different value (e.g. a UUID) - see WireMock | Response Templating | Random value helper for details.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论