简体   繁体   English

Mocking 一个 OpenFeign 客户端,用于 spring 库中的单元测试,而不是用于 spring 引导应用程序

[英]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 .首先使用@RequiredArgsConstructorUserServiceClient声明为 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM