[英]Mocking an OpenFeign client for Unit Testing in a spring library and NOT for a spring boot application
I've implemented a feign client that calls a get API based on this official repository .我已经实现了一个基于这个官方存储库调用 get API 的假客户端。 I have a rule class UserValidationRule
that needs to call that get API call getUser()
and validate some stuff.我有一个规则 class UserValidationRule
需要调用它来获取 API 调用getUser()
并验证一些东西。 That works as expected but when I get to testing that rule class, mocking the feign client is not successful and it continues to call the actual API.这按预期工作,但是当我开始测试该规则 class、mocking 时,伪装客户端不成功,它继续调用实际的 API。 I've simplified the situation so please ignore the simplicity lol.我已经简化了情况,所以请忽略简单性哈哈。 This is a follow up question I have after i found this stackoverflow question这是我发现这个stackoverflow问题后的后续问题
The API returns this model: API 返回此 model:
@Data
public class userModel {
private long id;
private String name;
private int age;
}
The interface with the rest client method:与rest客户端接口方法:
public interface UserServiceClient {
@RequestLine("GET /users/{id}")
UserModel getUser(@Param("id") int id);
}
And in the rule class, i build the feign client and call the API:在规则 class 中,我构建了 feign 客户端并调用 API:
@RequiredArgsConstructor
@Component
public class UserValidationRule {
private static final String API_PATH = "http://localhost:8080";
private UserServiceClient userServiceClient;
public void validate(String userId, ...) {
// some validations
validateUser(userId);
}
private void validateUser(String userId) {
userServiceClient = getServiceClient();
UserModel userModel = userServiceClient.gerUser(userId);
// validate the user logic
}
}
private UserServiceClient getServiceClient() {
return Feign.builder()
.encoder(new GsonEncoder())
.decoder(new GsonDecoder())
.target(UserServiceClient.class, API_PATH);
}
}
And here comes the test class:这里是测试 class:
public class UserValidationRuleTest {
private UserServiceClient userServiceClient = mock(UserServiceClient.class);
private UserValidationRule validationRule = new UserValidationRule();
private UserModel userModel;
@Before
public void init() {
userModel = generateUserModel();
}
@Test
public void validateWhenAgeIsNotBlank() {
doReturn(userModel).when(userServiceClient).getUser(any());
validationRule.validate("123", ...);
// some logic ...
assertEquals(.....);
verify(userServiceClient).getUser(any());
}
private UserModel generateUserModel() {
UserModel userModel = new UserModel();
userModel.setName("Cody");
userModel.setAge("22");
return accountModel;
}
}
As I debug validateWhenAgeIsNotBlank()
, i see that the userModel is not the one that's generated in the test class and the values are all null.当我调试validateWhenAgeIsNotBlank()
时,我看到 userModel 不是在测试 class 中生成的,并且值都是 null。 If I pass in an actual userId
, i get an actual UserModel that I have in my db.如果我传入一个实际的userId
,我会得到一个在我的数据库中的实际 UserModel 。
I think the problem is that UserServiceClient
is not being mocked.我认为问题在于UserServiceClient
没有被嘲笑。 The verify
is failing as it says the getUser()
is not invoked. verify
失败,因为它表示未调用getUser()
。 It might be something to do with how the feign client is declared in the UserValidationRule
with the feign.builder()... Please correct me if I'm wrong and tell me what I'm missing or any suggestions on how to mock it correctly.这可能与如何使用 feign.builder() 在UserValidationRule
中声明 feign 客户端有关...如果我错了,请纠正我并告诉我我遗漏了什么或关于如何模拟它的任何建议正确。
You are not using the spring managed UserServiceClient
bean.您没有使用 spring 托管的UserServiceClient
bean。 Every time you call UserValidationRule.validate
it calls validateUser
which in turn calls the getServiceClient
method.每次调用UserValidationRule.validate
时,它都会调用validateUser
,而后者又调用getServiceClient
方法。 This getServiceClient
creates a new instance of UserServiceClient
for each invocation.此getServiceClient
为每次调用创建一个新的UserServiceClient
实例。 This means when testing the mocked UserServiceClient
is not in use at all.这意味着在测试模拟的UserServiceClient
时根本没有使用。
I would restructure the code as below;我将重组代码如下;
First either declare UserServiceClient
as final with @RequiredArgsConstructor
or replace @RequiredArgsConstructor
with @AllArgsConstructor
.首先使用@RequiredArgsConstructor
将UserServiceClient
声明为 final 或将@RequiredArgsConstructor
替换为@AllArgsConstructor
。 The purpose of this change is to allow an instance of UserServiceClient
be injected rather than creating internally in the service method.此更改的目的是允许注入UserServiceClient
的实例,而不是在服务方法内部创建。
@Component
@RequiredArgsConstructor
public class UserValidationRule {
private final UserServiceClient userServiceClient;
.... // service methods
}
Then have a separate Configuration
class that builds the feign client as spring bean;然后有一个单独的Configuration
class 将 feign 客户端构建为 spring bean;
@Bean
private UserServiceClient userServiceClient() {
return Feign.builder()
.encoder(new GsonEncoder())
.decoder(new GsonDecoder())
.target(UserServiceClient.class, API_PATH);
}
At runtime this bean will now be injected into the UserValidationRule
.在运行时,这个 bean 现在将被注入到UserValidationRule
中。
As for the unit test changes you are creating the mock correctly but aren't setting/injecting that mock anywhere.至于单元测试更改,您正在正确创建模拟,但没有在任何地方设置/注入模拟。 You either need to use @Mock
and @InjectMocks
annotations or manually create instance of UserValidationRule
in your @Before
method.您要么需要使用@Mock
和@InjectMocks
注释,要么在@Before
方法中手动创建UserValidationRule
的实例。
Here is how @Mock
and @InjectMocks
use should look like;以下是@Mock
和@InjectMocks
使用方式;
@RunWith(MockitoJUnitRunner.class)
public class UserValidationRuleTest {
@Mock private UserServiceClient userServiceClient;
@InjectMocks private UserValidationRule validationRule;
... rest of the code
or continue using mock(...)
method and manually create UserValidationRule
.或继续使用mock(...)
方法并手动创建UserValidationRule
。
public class UserValidationRuleTest {
private UserServiceClient userServiceClient = mock(UserServiceClient.class);
private UserValidationRule validationRule;
private UserModel userModel;
@Before
public void init() {
validationRule = new UserValidationRule(userServiceClient);
userModel = generateUserModel();
}
... rest of the code
This will now ensure you are using single instance of spring managed feign client bean at runtime and mocked instance for the testing.现在,这将确保您在运行时使用 spring 托管 feign 客户端 bean 的单个实例和用于测试的模拟实例。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.