[英]Mocking an OpenFeign client for Unit Testing in a spring library and NOT for a spring boot application
我已經實現了一個基於這個官方存儲庫調用 get API 的假客戶端。 我有一個規則 class UserValidationRule
需要調用它來獲取 API 調用getUser()
並驗證一些東西。 這按預期工作,但是當我開始測試該規則 class、mocking 時,偽裝客戶端不成功,它繼續調用實際的 API。 我已經簡化了情況,所以請忽略簡單性哈哈。 這是我發現這個stackoverflow問題后的后續問題
API 返回此 model:
@Data
public class userModel {
private long id;
private String name;
private int age;
}
與rest客戶端接口方法:
public interface UserServiceClient {
@RequestLine("GET /users/{id}")
UserModel getUser(@Param("id") int id);
}
在規則 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);
}
}
這里是測試 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;
}
}
當我調試validateWhenAgeIsNotBlank()
時,我看到 userModel 不是在測試 class 中生成的,並且值都是 null。 如果我傳入一個實際的userId
,我會得到一個在我的數據庫中的實際 UserModel 。
我認為問題在於UserServiceClient
沒有被嘲笑。 verify
失敗,因為它表示未調用getUser()
。 這可能與如何使用 feign.builder() 在UserValidationRule
中聲明 feign 客戶端有關...如果我錯了,請糾正我並告訴我我遺漏了什么或關於如何模擬它的任何建議正確。
您沒有使用 spring 托管的UserServiceClient
bean。 每次調用UserValidationRule.validate
時,它都會調用validateUser
,而后者又調用getServiceClient
方法。 此getServiceClient
為每次調用創建一個新的UserServiceClient
實例。 這意味着在測試模擬的UserServiceClient
時根本沒有使用。
我將重組代碼如下;
首先使用@RequiredArgsConstructor
將UserServiceClient
聲明為 final 或將@RequiredArgsConstructor
替換為@AllArgsConstructor
。 此更改的目的是允許注入UserServiceClient
的實例,而不是在服務方法內部創建。
@Component
@RequiredArgsConstructor
public class UserValidationRule {
private final UserServiceClient userServiceClient;
.... // service methods
}
然后有一個單獨的Configuration
class 將 feign 客戶端構建為 spring bean;
@Bean
private UserServiceClient userServiceClient() {
return Feign.builder()
.encoder(new GsonEncoder())
.decoder(new GsonDecoder())
.target(UserServiceClient.class, API_PATH);
}
在運行時,這個 bean 現在將被注入到UserValidationRule
中。
至於單元測試更改,您正在正確創建模擬,但沒有在任何地方設置/注入模擬。 您要么需要使用@Mock
和@InjectMocks
注釋,要么在@Before
方法中手動創建UserValidationRule
的實例。
以下是@Mock
和@InjectMocks
使用方式;
@RunWith(MockitoJUnitRunner.class)
public class UserValidationRuleTest {
@Mock private UserServiceClient userServiceClient;
@InjectMocks private UserValidationRule validationRule;
... rest of the code
或繼續使用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
現在,這將確保您在運行時使用 spring 托管 feign 客戶端 bean 的單個實例和用於測試的模擬實例。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.