【问题标题】:How do I mock this client in Java?如何在 Java 中模拟这个客户端?
【发布时间】:2022-10-23 12:08:50
【问题描述】:

我正在尝试在 Java 中正确模拟这个标志客户端,但我不知道该怎么做。通常,我会通过 WireMock 模拟第三方 API(模拟调用),它会帮助我模拟它并为其添加测试。但是,实际的调用和逻辑被隐藏在这个客户端对象下,我不确定我是否正确地模拟了它。

以下是文档中的一些代码:https://docs.flagsmith.com/clients/server-side#initialise-the-sdk

我现在在我的代码库中有这个设置:

执行:

@Gateway
public class FlagsmithGateway implements FlagsmithPort {

    private final FlagsmithClient flagsmithClient;

    @Autowired
    public FlagsmithGateway(@Value("${flagsmith.environment.id}") String flagsmithEnvironmentId,
                            @Value("${flagsmith.endpoint}") String flagsmithEndpoint) {

        this(FlagsmithClient
            .newBuilder()
            .setApiKey(flagsmithEnvironmentId)
            .withApiUrl(flagsmithEndpoint)
            .build());
    }

    public FlagsmithGateway(FlagsmithClient flagsmithClient) {
        this.flagsmithClient = flagsmithClient;
    }

    @Override
    public boolean isEnabled(FeatureFlags flag) throws FlagsmithClientError {
        Flags flags = flagsmithClient.getEnvironmentFlags();
        return flags.isFeatureEnabled(flag.toString());
    }

    @Override
    public boolean isDisabled(FeatureFlags flag) throws FlagsmithClientError {
        Flags flags = flagsmithClient.getEnvironmentFlags();
        return !flags.isFeatureEnabled(flag.toString());
    }

}

上面实现的测试类:

@ExtendWith(MockitoExtension.class)
public class FlagsmithGatewayTest  {
    
    private FlagsmithGateway flagsmithGateway;

    @Mock
    private FlagsmithClient flagsmithClient;

    @BeforeEach
    public void setup() {
        flagsmithGateway = new FlagsmithGateway(flagsmithClient);
    }

    @Test
    public void isEnabled_shouldReturnWhetherFeatureIsEnabled() throws FlagsmithClientError {
        flagsmithClient = mock(FlagsmithClient.class);
        Flags flags = setupFlags("test_toggle", true);

        when(flagsmithClient.getEnvironmentFlags()).thenReturn(flags);=

        boolean result = flagsmithGateway.isEnabled(FeatureFlags.TOGGLE_FOR_TESTS); //FlagsmithGateway is now null
        assertThat(result).isTrue();
    }

    private static Flags setupFlags(String featureName, Boolean enabled) {
        Flags flag = new Flags();
        BaseFlag baseFlag = new BaseFlag();
        Map<String, BaseFlag> someFlags = new HashMap<>();

        baseFlag.setFeatureName(featureName);
        baseFlag.setEnabled(enabled);
        someFlags.put(featureName,baseFlag);
        flag.setFlags(someFlags);

        return flag;
    }
}

虽然上面的代码通过了,但它实际上并没有测试网关。我尝试通过从网关代码调用该方法进行测试,但我遇到了模拟该行或 NPE 的问题。我如何正确测试这个标志客户端?谢谢!

【问题讨论】:

    标签: java spring-boot unit-testing mocking mockito


    【解决方案1】:

    要测试FlagsmithGateway,您只需要验证其方法是否与其依赖项(即FlagsmithClient)正确交互,例如它是否真的使用预期参数调用预期方法。在这种情况下,您只需要模拟@ 987654323@ 并存根它的getEnvironmentFlags()。

    为了让 FlagsmithGateway 能够使用模拟的 FlagsmithClient ,您需要有一些方法将 FlagsmithClient 传递给它。 (例如通过构造函数或设置器)。因此,首先,我会稍微重构您的网关,以便它允许直接通过构造函数使用FlagsmithClient 创建。

    @Gateway
    public class FlagsmithGateway implements FlagsmithPort {
    
        private final FlagsmithClient flagsmithClient; 
    
        @Autowired
        public FlagsmithGateway(@Value("${flagsmith.apikey}") String flagsmithApiKey,
                                @Value("${flagsmith.endpoint}") String flagsmithEndpoint) {
            this(FlagsmithClient
                .newBuilder()
                .setApiKey(flagsmithApiKey)
                .withApiUrl(flagsmithEndpoint)
                .build());
        }
    
        public FlagsmithGateway(FlagsmithClient flagsmithClient) {
            this.flagsmithClient = flagsmithClient;
        }
    
    }
    

    并且测试将使用此构造函数来创建 FlagsmithGateway :

    @ExtendWith(MockitoExtension.class)
    public class FlagsmithGatewayTest extends GatewayIntegrationTest {
    
        FlagsmithGateway flagsmithGateway;
    
        @Mock
        FlagsmithClient flagsmithClient;
    
        @Before
        public void setup() {
            flagsmithGateway = new FlagsmithGateway(flagsmithClient);
        }
    
        @Test
        public void isEnabled_shouldReturnWhetherFeatureIsEnabled() throws FlagsmithClientError {
    
            Flags flags = defaultFlagHandler("test_toggle", true);
            when(client.getEnvironmentFlags()).thenReturn(flags);
            
            FeatureFlags featureFlags = xxxxx //create it based on your logic
    
            boolean result = flagsmithGateway.isEnabled(featureFlags);
            assertThat(result).isTrue();
        }
    
    }
    

    请注意,我使用普通的 Mockito 测试手动创建 FlagsmithGateway 但不使用 spring-boot-test 主要是因为您在 spring 中设置的方式将使其使用构造函数

    new FlagsmithGateway(String flagsmithApiKey,String flagsmithEndpoint) 
    

    创建一个FlagsmithGateway,但它总是硬编码以使用真正的FlagsmithClient 实例。

    如果您真的想验证原始构造函数中的代码是否按预期工作,您可以为其创建另一个测试:

    @Test
    public void flagsmithClientCreatedProperly(){
       FlagsmithGateway gateway = new FlagsmithGateway("apiKey" , "http://foo");
    
      //get the FlagsmithClient from  FlagsmithGateway and the assert tis apiKey and apiUrl 
    }
    

    【讨论】:

    • FlagsmithClient 仍然为空。我在上面做错了什么?
    • @Euridice01 看起来模拟没有被初始化。试试FlagsmithClient flagsmithClient = Mockito.mock(FlagsmithClient .class);。而且...您在测试中使用 Spring 吗?为什么要在简单的事情上增加如此多的复杂性?
    • 我添加了该行,但是当它转到该行时,它失败了: flags.isFeatureEnabled(flag.toString());因为 Flags 没有被正确地模拟。我很高兴从头开始测试并了解如何正确设置它。遇到很多困难。
    • @Euridice01,您如何在测试中创建 FlagsmithGateway?我建议使用新的构造函数来创建它,并将模拟的FlagsmithClient 显式传递给它。我刚刚更新了我的答案以更清楚地提到它。请检查。
    • 忘了在我原来的答案中提到,为了使@Mock生效,你必须确保@ExtendWith(MockitoExtension.class)在你的测试类上被注释
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-04-04
    • 1970-01-01
    • 2015-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多