英文:
How to mock dependencies that depend on input value?
问题
我想对这样一个方法进行单元测试:
public String handleRequest(Event event) {
    for(Message msg : event.getRecords()){
        SDKClient client = new SDKClient(msg.getUser(), msg.getPassword());
        String output = client.makeAPICall();
        return output.toUpperCase();
    }
}
通常情况下,我们会通过将依赖项(如 SDKClient)作为参数传递,并在 Junit/Mockito 中进行模拟。但是,在这种情况下,我不能简单地传递它,因为 SDKClient 依赖于传入的实际事件。而且,每个事件中的每条消息都需要一个单独的客户端,数量不确定。我想将整个方法作为一个单元进行测试,但我事先不知道依赖关系。这种情况是否有可能?
英文:
I want to unit-test a method like this:
public String handleRequest(Event event) {
      for(Message msg : event.getRecords()){
           SDKClient client = new SDKClient(msg.getUser(), msg.getPassword());
           String output = client.makeAPICall();
           return output.toUpperCase();
       }
   }
}
Typically, we mock dependencies like SDKClient by passing them in as arguments and mocking them in Junit/Mockito. However, in this case, I cannot just pass it because the SDKClient depends on the actual events passed in. There is also an undetermined number of clients, one for each message in the event. I want to unit test the method as a whole, but I do not know the dependencies in advance. Is it possible?
答案1
得分: 1
在这种情况下,您传入的是一种抽象new SDKClient调用的函数:
interface SdkClientProvider {
    SDKClient(String user, String password);
}
在这种特定情况下,您可以使用BiFunction<String, String, SDKClient>。
由于new SDKClient很可能是这个类唯一的“活”实现,您甚至可以这样做:
class MyService {
    @Setter
    private BiFunction<String, String, SDKClient> createClient = SDKClient::new;
    ...
}
在依赖于系统java.time.Clock的情况下,经常看到这种模式。
(请注意,“创建、使用和处理服务对象”是一个非常值得质疑的设计,除非您被迫使用一些设计不良的外部库,否则最好进行重构。)
英文:
In that case, what you pass in is some kind of function that abstracts the new SDKClient call:
interface SdkClientProvider {
    SDKClient(String user, String password);
}
In this particular case, you could use BiFunction<String, String, SDKClient> if you prefer.
As new SDKClient is likely to be the only "live" implementation of this class, you can even do something like this:
class MyService {
    @Setter
    private BiFunction<String, String, SDKClient> createClient = SDKClient::new;
    ...
}
This pattern is commonly seen when depending on the system java.time.Clock.
(Note that the "create, use, and dispose of a service object" is a very questionable design, and unless you're stuck with consuming some badly-designed external library, it's begging for refactoring.)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论