我正在使用Java 6,JUnit 4.8.1,并编写控制台应用程序。我的应用程序有一个未公开的成员字段...
public class MyApp {
...
private OpportunitiesService m_oppsSvc;
private void initServices() {
…
m_oppsSvc = new OpportunitiesServiceImpl(…);
}
...
}
我想模拟一种行为,这样无论何时调用我的服务中的一个方法(例如m_oppsSvc.getResults()
),总会返回相同的结果。我怎么做?这个领域没有setter方法。我目前正在使用Mockito 1.8.4。可以用Mockito或其他一些模拟框架来做到这一点吗?
答案 0 :(得分:12)
这就是你想要的:
@RunWith(MockitoJUnitRunner.class)
public class MyAppTest {
@Mock private OpportunitiesService mocked_m_oppsSvc;
@InjectMocks MyApp myApp;
@Test public void when_MyApp_uses_OpportunititesService_then_verify_something() {
// given
given( mocked_m_oppsSvc.whatever()).willReturn(...);
// when
myApp.isUsingTheOpportunitiesService(...);
// then
verify...
assertThat...
}
}
使用:Mockito 1.9.0,BDD style, FEST-Assert AssertJ。
希望有所帮助:)
答案 1 :(得分:8)
鉴于你已经在使用mockito,为什么不使用反射:
@RunWith(MockitoJUnitRunner.class)
public class MyApp {
@Mock
private OpportunitiesService m_oppsSvc;
private MyApp myApp;
@Before
public void before() throws Exception {
myApp = new MyApp();
Field f = MyApp.class.getDeclaredField("m_oppsSvc");
f.setAccessible(true);
f.set(myApp, m_oppsSvc);
}
}
这有点难看,但它会起作用。请注意,这可能不是使用Mockito执行此操作的最有效方法,但它可以正常工作。
还有Powermock,你可以使用Whitebox类来完成这项工作。我不会深入了解Powermock的所有细节,但这里是注入私有字段值的调用,它应该是一个模拟对象:
Whitebox.setInternalState(myApp, "m_oppsSvc", m_oppsSvc);
答案 2 :(得分:6)
您应该考虑尝试模拟私有字段smell。也就是说,表示您要执行的操作不正确或者您的代码当前结构不正确。您应该只需要模拟公共方法或注入依赖项
在您提供的代码中,您应该考虑按如下方式注入OpportunitiesService:
public class MyApp {
...
private OpportunitiesService m_oppsSvc;
public MyApp(OpportunitiesService oppsSvc) {
this.m_oppsSvc = oppsSvc;
}
...
}
在测试中,您可以按如下方式注入模拟:
OpportunitiesService mockOpportunitiesService =
Mockito.mock(OpportunitiesService.class);
Mockit.when(mockOpportunitiesService.someMethod()).thenReturn(someValue);
MyApp app = new MyApp(mockOpportunitiesService);
答案 3 :(得分:0)
您可以使用JMockit轻松完成:
public class MyAppTest
{
@Tested MyApp myApp;
@Test
public testSomething(final @Capturing OpportunitiesService mockService)
{
new NonStrictExpectations() {{
mockService.getResults(); result = asList("a", "b", "C");
// record other expectations, if needed
}};
myApp.whateverMethodIWantToTest();
new Verifications() {{
mockService.doSomething(anyInt);
// verify other expectations, if needed
}};
}
}
即使测试代码中没有提到实现类OpportunitiesServiceImpl
,它的实例(任意数量)仍然会被正确模拟。
答案 4 :(得分:-1)
通常,您应该使用依赖注入并通过构造函数,单独的setter或直接向方法(getResults)传递模拟对象(类型为OppportunitiesServiceImpl)。您可能需要首先为OpportunitiesServiceImpl提取接口。
答案 5 :(得分:-1)
通常,这是通过使用依赖注入来解决的。在常规(生产)模式下,依赖注入容器(例如Spring或Guice)将通过构造函数或通过setter将OpportunitiesService
的实例注入MyApp
。
然后,当您进行测试时,您可以使用相同的setter或构造函数参数手动“注入”模拟实例。
而不是做
m_oppsSvc = new OpportunitiesServiceImpl(…);
尝试通过OpportunitesService
的构造函数
MyApp