Rspec 如何使用powermock部分模拟外部方法调用

Rspec 如何使用powermock部分模拟外部方法调用,rspec,junit,tdd,powermock,powermockito,Rspec,Junit,Tdd,Powermock,Powermockito,我有两门课。 说出ServiceLayer.class和apiadapter.class: public class ApiAdaptor { public String getCountry(String latitude, String longitude) { // REST call to api goes here and a the country string is returned return country; } } public class Ser

我有两门课。 说出
ServiceLayer.class
apiadapter.class

public class ApiAdaptor {
  public String getCountry(String latitude, String longitude) {
    // REST call to api goes here and a the country string is returned
    return country;
  }
}

public class ServiceLayer {
 public String findCountry(int positionId) {
   // some business logic here to get latitude and longitude from postitionId 
   ApiAdaptor api = new ApiAdaptor();
   String country = api.getCountry(latitude, longitude);
   return country;
 }
}

现在在单元测试中,我只想测试这个方法
ServiceLayer.findcountry()
,同时模拟对
apiadapter.getCountry(纬度、经度)
的内部调用。是否有任何方法可以使用Powermock实现这一点。在RubyonRails中使用Rspec时,我见过类似类型的存根。我也想在我的java SpringMVC项目中进行类似的测试。

当然,您可以使用PowerMock只关注该方法。例如,专门使用PowerMockito,您可以编写以下测试:

@RunWith(PowerMockRunner.class)
@PrepareForTest( {ServiceLayer.class} )
public class PowerMockitoJan10Test {
    private static final java.lang.String DESIRED_COUNTRY_VALUE = "USA";

    @Test
    public void testServiceLayerFindCountry() throws Exception {
        ApiAdaptor mock = Mockito.mock(ApiAdaptor.class);
        PowerMockito.whenNew(ApiAdaptor.class).withAnyArguments().thenReturn(mock);
        Mockito.when(mock.getCountry(Mockito.anyString(), Mockito.anyString())).thenReturn(DESIRED_COUNTRY_VALUE);

        String country = new ServiceLayer().findCountry(1);
        Assert.assertEquals(DESIRED_COUNTRY_VALUE, country);
    }
}
如果您使用Spring,很可能您也需要一个JUnit运行程序来实现这一点,因此您可以为PowerMockito使用JUnit规则--


编辑:这很有趣。使用该规则时,除非将
ServiceLayer.class
添加到
@PrepareForTest
列表中,否则它实际上不起作用。在撰写本文时,我使用了最新的PowerMockito版本1.6.4。可能值得报道。在任何情况下,您的测试都将使用Spring:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("mycontext.xml")
@PrepareForTest({ApiAdaptor.class, ServiceLayer.class})
public class PowerMockitoJan10_WithRuleTest {
    private static final String DESIRED_COUNTRY_VALUE = "USA";

    @Rule
    public PowerMockRule rule = new PowerMockRule();

    @Test
    public void testServiceLayerFindCountry() throws Exception {
        PowerMockito.whenNew(ApiAdaptor.class).withNoArguments().thenReturn(new ApiAdaptor() {
            @Override
            public String getCountry(String latitude, String longitude) {
                return DESIRED_COUNTRY_VALUE;
            }
        });

        String country = new ServiceLayer().findCountry(1);
        Assert.assertEquals(DESIRED_COUNTRY_VALUE, country);
    }

}
或者,如果重写是一个问题,您可以模拟apiadapter:

    ...
    ApiAdaptor mock = PowerMockito.mock(ApiAdaptor.class);
    PowerMockito.when(mock.getCountry(Mockito.anyString(), Mockito.anyString())).thenReturn(DESIRED_COUNTRY_VALUE);
    PowerMockito.whenNew(ApiAdaptor.class).withNoArguments().thenReturn(mock);
    ...

如果您可以更改代码,我建议您通过在类上进行依赖项注入来提高代码的可测试性

你会有这样的想法:

public class ServiceLayer {
    private ApiAdaptor _api;
    public ServiceLayer(ApiAdaptor api) {
        _api = api;
    }
    [snip]
}
然后在代码的其余部分使用
\u api

当您需要测试此类时,如果您必须模拟
ApiAdaptor
,您现在可以编写:

ApiAdaptor mock = Mockito.mock(ApiAdaptor.class);
[Api adaptor behavior mocking]
String country = new ServiceLayer(mock).findCountry(1);
[asserts and all]

这消除了对PowerMockito、其运行程序和/或规则的需要…

我也在做同样的事情,但是当我运行测试时,存根方法没有被调用,完成的方法运行…:(我的注释配置如下:@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration@PrepareForTest({ServiceLayer.class})你是对的,当切换到规则时,它不会像那样工作。但是我将
ServiceLayer.class
添加到注释中的列表中,它现在工作得很好。只是作为一个示例尝试了一下,它就工作了。这是我的代码:Thnks for the Suggestions…我目前正在进行依赖项注入,但我觉得在将来的某个时候我会将不得不使用power mock,因为我不能继续声明类变量…这就是为什么我要寻找一个特定于power mock或类似库的解决方案。如果您开始有太多依赖项,也许它可以帮助添加一个将一些变量保存在一起的类。某些依赖项作为方法参数可能比CLA更有意义它的依赖性。