假设我有以下想要模拟的界面:
Searcher.java
public interface Searcher {
public String search();
public void someMethod();
}
当我想为此界面使用不同的模拟实现时,我该怎么办?例如,在一个测试中,我想让search()
方法返回空字符串,在另一个测试中我希望它开始做一些HTTP请求等。
我是否封装了行为,就像fx一样。将它放在SearchBehaviour接口中,然后为该接口编写实现:
public class SearcherMock implements Searcher {
private SearchBehaviour searchBehaviour;
public SearcherMock(SearchBehaviour searchBehaviour) {
this.searchBehaviour = searchBehaviour;
}
@Override
public String search() {
return searchBehaviour.search();
}
@Override
public void someMethod() {
// Do something here
}
}
或者我是否为每个模拟实现创建一个新的模拟类? FX。 EmptySearcher
和HTTPSearcher
?
答案 0 :(得分:3)
我建议您使用java模拟工具,例如jMock或Mockito,这样可以节省您一些时间,不让您自己编写模型工具,而是编写好的模型:)
使用Mockito你可以做这样的事情(虽然没有经过测试):
import static org.mockito.Mockito.*;
Searcher mockedEmptySearcher = mock(Searcher.class);
// define how empty searcher should behave
when(mockedList.search()).thenReturn("");
答案 1 :(得分:2)
请记住,您可以在不使用库的情况下为不同的测试自由创建匿名内部类:
@Test public void test1() {
// When referring to outside local variables, they must be final.
final AtomicBoolean someMethodCalled = new AtomicBoolean(false);
Searcher fakeSearcher = new Searcher() {
@Override public String search() {
return "stubbed return value";
}
@Override public void someMethod() {
someMethodCalled.set(true);
}
};
SystemUnderTest systemUnderTest = new SystemUnderTest(fakeSearcher);
systemUnderTest.pressBigRedButton();
assertTrue("someMethod should have been called", someMethodCalled.get());
}
甚至变得非常聪明:
private Searcher createFakeSearcher(final String... searchResults) {
return new Searcher() {
int returnIndex = 0;
@Override public String search() {
return searchResults[returnIndex++];
}
@Override public void someMethod() {}
};
}
但是一些时间投入学习一个模拟框架将很好地为你和你的测试服务,因为像Mockito这样的框架旨在剥离那个样板:
// Uses static imports from org.mockito.Mockito;
@Test public void test1() {
Searcher mockSearcher = mock(Searcher.class);
when(mockSearcher.search())
.thenReturn("search one")
.thenReturn("search two")
.thenThrow(new IllegalStateException());
SystemUnderTest systemUnderTest = new SystemUnderTest(mockSearcher);
systemUnderTest.pressBigRedButton();
verify(mockSearcher, times(2)).someMethod();
}
为了更好地概念性地介绍测试双打(假人/存根/模拟/假货)以及它们之间的差异,请阅读Martin Fowler's article here或直接进入Mockito documentation
答案 2 :(得分:2)
您可以使用像Mockito这样的模拟框架,并使用JUnitParams参数化您的测试。
假设您有一个验证Searcher的类,如果搜索返回'valid',它将通过。代码和测试如下所示,有2个测试用例,1个用于有效,1个用于无效的搜索结果。
public class SomeClass {
public boolean isValid(Searcher searcher) {
return searcher.search().equals("valid");
}
}
@RunWith(JUnitParamsRunner.class)
public class SomeClassTest {
public Object[] provideIsValid() {
return new Object[]{
new Object[]{ "invalid", false },
new Object[]{ "valid", true }
};
}
@Test
@Parameters(method = "provideIsValid")
public void testIsValid(String output, String expected) {
SomeClass someClass = new SomeClass();
Searcher mock = mock(Searcher.class);
when(mock.search()).thenReturn(output);
String actual = someClass.isValid(mock);
assertEquals(expected, actual);
}