Java:当该字段未公开时,如何模拟字段的方法?

时间:2012-07-20 20:35:44

标签: java unit-testing junit mockito

我正在使用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或其他一些模拟框架来做到这一点吗?

6 个答案:

答案 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.0BDD 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