16

我正在使用 Java 6、JUnit 4.8.1 并编写控制台应用程序。我的应用程序有一个未公开的成员字段……</p>

public class MyApp { 
    ...
    private OpportunitiesService m_oppsSvc;

    private void initServices() { 
        …
        m_oppsSvc = new OpportunitiesServiceImpl(…);
    }
    ...
}

我想模拟一种行为,这样每当我的服务中的一个方法被调用(例如m_oppsSvc.getResults())时,总是返回相同的结果。我怎么做?该字段没有设置方法。我目前正在使用 Mockito 1.8.4。是否可以使用 Mockito 或其他一些模拟框架来做到这一点?

4

6 回答 6

13

这就是你想要的:

@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.0BDD 风格FEST-Assert AssertJ

希望有帮助:)

于 2012-07-20T22:55:22.127 回答
10

鉴于您已经在使用 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);
于 2012-07-21T01:17:41.667 回答
6

您应该将模拟私有字段的尝试视为一种气味。也就是说,这表明您尝试执行的操作不正确,或者您的代码当前结构不正确。您应该只需要模拟公共方法或注入依赖项

在您提供的代码中,您应该考虑注入 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);
于 2012-07-20T20:41:18.703 回答
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在测试代码中没有提到实现类,它的实例(任意数量)仍然会被正确地模拟。

于 2012-07-20T20:52:12.657 回答
-1

通常,您应该使用依赖注入并通过构造函数、单独的设置器或直接将模拟对象(OppportunitiesServiceImpl 类型)传递给方法(getResults)。您可能需要先为 OpportunitiesServiceImpl 提取一个接口。

于 2012-07-20T20:39:04.920 回答
-1

通常,这是通过使用依赖注入来解决的。在常规(生产)模式下,您的依赖注入容器(例如 Spring 或 Guice)将通过您的构造函数或通过 setterOpportunitiesService注入in 的实例。MyApp

然后,当您进行测试时,您可以使用相同的 setter 或构造函数参数手动“注入”模拟实例。

而不是做

m_oppsSvc = new OpportunitiesServiceImpl(…);

尝试OpportunitesService通过MyApp的构造函数传入

于 2012-07-20T20:39:45.490 回答