如何使用不同的 id 两次模拟 webclient uri(相同的 uri 和不同的变量)
我有这个 webClient 模拟,如果我只模拟单个 ID,它就可以工作。但在这里我将有一个程序调用两个不同的帐户并执行一些操作,因此我需要像下面的代码一样调用 webClient 两次,如果此代码有效,我将能够编写主要测试。
@AutoConfigureWebTestClient
@ExtendWith(MockitoExtension.class)
@AutoConfigureWebFlux
@SpringBootTest(classes = Application.class)
class AccountServiceTest {
@Autowired
private AccountServiceInterface accountService;;
@Mock
WebClient webClientMock;
@Mock
WebClient.RequestHeadersUriSpec requestHeadersUriSpecMock;
@Mock
WebClient.RequestHeadersSpec requestHeadersSpecMock;
@Mock
WebClient.ResponseSpec responseSpecMock;
@BeforeEach
void setUp() {
ReflectionTestUtils.setField(accountService, "webClient", webClientMock);
}
private void mockAccount(Long accountId){
AccountGradeDto mockAccount = new AccountGradeDto(accountId, 1L, 1L, new BigDecimal(1000), AccountStatus.ACTIVE, 10L);
when(webClientMock.get()).thenReturn(requestHeadersUriMock);
when(requestHeadersUriMock.uri("/api/account/management/v1/get-account-grade/{id}", accountId)).thenReturn(requestHeadersSpecMock);
when(requestHeadersSpecMock.retrieve()).thenReturn(responseSpecMock);
when(responseSpecMock.onStatus(any(), any())).thenReturn(responseSpecMock);
when(responseSpecMock.bodyToMono(AccountGradeDto.class)).thenReturn(Mono.just(mockAccount));
}
@Test
public void testAccountService() {
mockAccount(1L);
mockAccount(2L);
StepVerifier.create(accountService.getAccountById(2L))
.expectNextMatches(account -> account.getId().equals(2L))
.verifyComplete();
StepVerifier.create(accountService.getAccountById(1L))
.expectNextMatches(account -> account.getId().equals(1L)) //TEST FAILS HERE AS IT RETURNS SAME OBJECT
.verifyComplete();
}
这是
@Service
public class AccountServiceImpl implements AccountServiceInterface {
private final WebClient webClient;
public AccountServiceImpl(@Value("${app.resource.account.service-base-url}") String baseUrl) {
this.webClient = WebClient.create("http://" + baseUrl);
}
@Override
public Mono<AccountGradeDto> getAccountById(Long accountId) {
return webClient.get()
.uri("/api/account/management/v1/get-account-grade/{id}", accountId)
.retrieve()
.onStatus(HttpStatus.NOT_FOUND::equals, clientResponse -> Mono.empty())
.bodyToMono(AccountGradeDto.class).transform(ReactiveUtils::errorIfEmpty);
}
}
当我运行测试时如果在第二次匹配(id:1L)失败时的服务实现,因为它返回为 ID 2 模拟的相同对象
I have this webClient mock and it works if I only mock single ID. but here I will have a procedure that calls two different accounts and do some stuffs so I need to call a webClient twice like the blow code if this code works I'll be able to write the main test.
@AutoConfigureWebTestClient
@ExtendWith(MockitoExtension.class)
@AutoConfigureWebFlux
@SpringBootTest(classes = Application.class)
class AccountServiceTest {
@Autowired
private AccountServiceInterface accountService;;
@Mock
WebClient webClientMock;
@Mock
WebClient.RequestHeadersUriSpec requestHeadersUriSpecMock;
@Mock
WebClient.RequestHeadersSpec requestHeadersSpecMock;
@Mock
WebClient.ResponseSpec responseSpecMock;
@BeforeEach
void setUp() {
ReflectionTestUtils.setField(accountService, "webClient", webClientMock);
}
private void mockAccount(Long accountId){
AccountGradeDto mockAccount = new AccountGradeDto(accountId, 1L, 1L, new BigDecimal(1000), AccountStatus.ACTIVE, 10L);
when(webClientMock.get()).thenReturn(requestHeadersUriMock);
when(requestHeadersUriMock.uri("/api/account/management/v1/get-account-grade/{id}", accountId)).thenReturn(requestHeadersSpecMock);
when(requestHeadersSpecMock.retrieve()).thenReturn(responseSpecMock);
when(responseSpecMock.onStatus(any(), any())).thenReturn(responseSpecMock);
when(responseSpecMock.bodyToMono(AccountGradeDto.class)).thenReturn(Mono.just(mockAccount));
}
@Test
public void testAccountService() {
mockAccount(1L);
mockAccount(2L);
StepVerifier.create(accountService.getAccountById(2L))
.expectNextMatches(account -> account.getId().equals(2L))
.verifyComplete();
StepVerifier.create(accountService.getAccountById(1L))
.expectNextMatches(account -> account.getId().equals(1L)) //TEST FAILS HERE AS IT RETURNS SAME OBJECT
.verifyComplete();
}
and here is the service implementation
@Service
public class AccountServiceImpl implements AccountServiceInterface {
private final WebClient webClient;
public AccountServiceImpl(@Value("${app.resource.account.service-base-url}") String baseUrl) {
this.webClient = WebClient.create("http://" + baseUrl);
}
@Override
public Mono<AccountGradeDto> getAccountById(Long accountId) {
return webClient.get()
.uri("/api/account/management/v1/get-account-grade/{id}", accountId)
.retrieve()
.onStatus(HttpStatus.NOT_FOUND::equals, clientResponse -> Mono.empty())
.bodyToMono(AccountGradeDto.class).transform(ReactiveUtils::errorIfEmpty);
}
}
when I run the test if fails at second match (id: 1L) as it returns the same objects mocked for ID 2
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我们不应该使用单例范围来模拟
WebClient.RequestHeadersSpec
和WebClient.ResponseSpec
并且需要对同一 uri 上的每个请求进行模拟。we should not use singleton scope for mocking
WebClient.RequestHeadersSpec
andWebClient.ResponseSpec
and need to be mocked for every request on same uri.模拟 WebClient 可能非常棘手且容易出错。即使您可以涵盖一些简单的积极案例,也很难测试更复杂的场景,例如错误处理、重试……
我建议 WireMock 它为测试 Web 客户端提供了一个非常好的 API。以下是一些示例
来轻松测试正面和负面场景
您可以通过提供不同的存根或使用 场景 测试重试逻辑 甚至使用 延迟 来模拟超时
Mocking WebClient could be very tricky and error-prone. Even if you could cover some simple positive cases, it would be very hard to test more complex scenarios, like error handling, retries, ...
I would recommend WireMock which provides a very good API for testing web clients. Here are some examples
You could easily test both positive and negative scenarios by providing different stubs
or test retry logic using Scenarios or even simulate timeouts using Delays