我正在编写一个测试套件,我正在考虑如何模拟某些请求/响应流。例如,我想测试一个进行多个RESTful调用的方法:
getCounts() {
...
Promise<Integer> count1 = getCount1();
Promise<Integer> count2 = getCount2();
// returns a DataModel containing all counts when the Promises redeem
}
getCount1() {
...
Request<Foo> request = new Request<Foo>();
sendRequest(request);
...
}
getCount2() {
...
Request<Bar> request = new Request<Bar>();
sendRequest(request);
...
}
sendRequest(Request<T> request) {...}
然而,每个getCount()
方法创建一个不同的Request<T>
对象,其中<T>
描述了正在进行的请求类型,涉及到正在检索的计数。这意味着我不能简单地"模拟"sendRequest()
方法,因为每次都以不同的类型调用它。
我正在考虑一种方法,我注册一个"处理程序"…当调用sendRequest()
时,它决定调用哪个处理程序,处理程序将知道要返回的模拟数据的适当类型。注册类似于存储处理程序类类型或处理程序类的实例以及所需的模拟数据,当调用sendRequest()
时,它将查找并调用正确的处理程序。
然而,我不确定这是否是一个好的模式,我想知道是否有更好的方法来解决这个问题。注册一个类或一个特定的方法以便以后执行特定的任务,一个好的模式是什么?
在没有更多上下文的情况下很难回答,但是一般的方法是使用控制反转(IOC)。例如,将getCountXXX
方法放入它们自己的类中,这可能是一个更好的重用、可读性、封装性、可测试性等方面的好主意:
public class CountFetcher {
getCount1() { ... }
getCount2() { ... }
}
原始代码现在使用任何可用的"注入"机制获得CountFetcher
的实例。最简单的只是一个构造函数:
public class Counter {
private final CountFetcher fetcher;
public Counter(CountFetcher fetcher) {
this.fetcher = fetcher;
}
public getCounts() {
Promise<Integer> count1 = fetcher.getCount1();
Promise<Integer> count2 = fetcher.getCount2();
...
}
}
在您的产品代码中,您使用实际的CountFetcher
实例化Counter
。在测试代码中,您注入CountFetcher
的模拟版本,它可以让每个单独的getCountXXX
方法返回您想要的任何内容:
public class MockCountFetcher extends CountFetcher {
@Override
getCount1() { return mockCount1; }
}
public class TestCounter {
@Test
public void smokeTest() {
CountFetcher mockFetcher = new MockCountFetcher();
Counter counter = new Counter(mockFetcher);
assertEquals(someExpectedValue, counter.getCounts());
}
}