1

我已经实现了一个基于这个官方存储库调用 get API 的 feign 客户端。我有一个规则类UserValidationRule需要调用获取 API 调用getUser()并验证一些东西。这可以按预期工作,但是当我开始测试该规则类时,模拟 feign 客户端并不成功,它会继续调用实际的 API。我已经简化了情况,所以请忽略简单性哈哈。这是我发现这个stackoverflow问题后的后续问题

API 返回此模型:

@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);
}

在规则类中,我构建了 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);
    }
}

测试类来了:

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 不是在测试类中生成的,并且值都是空的。如果我传入一个实际的userId,我会得到一个我在我的数据库中拥有的实际 UserModel。

我认为问题在于UserServiceClient没有被嘲笑。verify失败,因为它说没有getUser()被调用。这可能与如何在UserValidationRulefeign.builder() 中声明 feign 客户端有关...如果我错了,请纠正我并告诉我我遗漏了什么或有关如何正确模拟它的任何建议.

4

1 回答 1

2

您没有使用 spring 托管UserServiceClientbean。每次您调用UserValidationRule.validate它时,它都会调用validateUsergetServiceClient方法。这会为每次调用getServiceClient创建一个新的实例。UserServiceClient这意味着在测试模拟时UserServiceClient根本不使用。

我将重组代码如下;

首先声明UserServiceClient为 final with@RequiredArgsConstructor或替换@RequiredArgsConstructor@AllArgsConstructor。此更改的目的是允许UserServiceClient注入实例,而不是在服务方法内部创建。

@Component
@RequiredArgsConstructor
public class UserValidationRule {

    private final UserServiceClient userServiceClient;

.... // service methods

}

然后有一个单独的Configuration类,将 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手动创建实例。UserValidationRule@Before

以下是使用方式@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 的单个实例,并使用模拟实例进行测试。

于 2021-03-22T17:25:35.853 回答