【问题标题】:How to test a static method using mock() and spy()如何使用 mock() 和 spy() 测试静态方法
【发布时间】:2018-02-26 20:28:34
【问题描述】:

以下代码部分中发布的方法包含一个静态方法,即“with()”。我想测试下面的代码,所以我编写了这个方法的测试 如测试部分所示。

我尝试同时使用“spy()”和“mock()”测试该方法,但测试总是失败。

请告诉我如何测试方法返回 void?

代码

 public RequestCreator requestCreatorFromUrl(String picUrl) {
    return Picasso.with(mCtx).load(picUrl);
}

测试

public class ValidationTest {
@Mock
private Context mCtx = null;
@Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();

@Before
public void setUp() throws Exception {
    mCtx = Mockito.mock(Context.class);
    Assert.assertNotNull("Context is not null", mCtx);
}

 @Test
public void whenRequestCreatorFromUrlTest() throws Exception {
    Picasso picasso = Picasso.with(mCtx);
    Picasso spyPicasso = spy(picasso);
    Uri mockUri = mock(Uri.class);
    RequestCreator requestCreator = Picasso.with(mCtx).load(mockUri);
    RequestCreator spyRequestCreator = spy(requestCreator);

    doReturn(spyRequestCreator).when(spyPicasso.load(mockUri));
    //when(spyPicasso.load(mockUri)).thenReturn(spyRequestCreator);
    RequestCreator actual = spyPicasso.load(mockUri);

    Assert.assertEquals(requestCreator, actual);
}

【问题讨论】:

  • 当然:默认答案是 - 如果可能,将您的生产代码更改为需要模拟静态方法。
  • @GhostCat,同意,尽可能避免模拟静态方法的需要。但是 Letsamrit 在下面的 cmets 中写道,因为 Picasso 类来自我正在使用的外部库。我认为在这种情况下,模拟静态的东西是必要的。
  • @mxf 取决于您愿意投入多少努力。你可以总是创建你自己的interface来包裹这些功能。然后你提供一个小的 impl 类,它只调用静态的东西。这也有助于您从外部库中去耦合您的逻辑。
  • @GhostCat,谢谢,很好的解决方案。
  • @mxf 当然 ;-) ...而且很可能,您会找到我的答案之一...已经在某个地方准确地说出来了。

标签: android unit-testing junit mockito powermockito


【解决方案1】:

通常,如果您最终使用PowerMock,这是一个好兆头,表明您很可能走错路了。

如果不是直接引用Picasso,而是创建一个组件,它的职责是加载图像,假设类ImageLoader。这会给你带来什么?

  • 关注点分离:如果明天你决定转移到Glide,你不应该改变你使用Picasso的每一个类,你只需改变ImageLoader的实现。其他组件对这些更改并不明智,因为它们依赖于抽象,依赖于实现

  • Seam:这将允许您轻松地模拟依赖项以执行单元测试

这将是我们的抽象:

interface ImageLoader { RequestCreator load(String url); }

让我们提供一个实现:

class ImageLoaderImpl implements ImageLoader { private final Picasso picasso; public ImageLoaderImpl(Context context) { this.picasso = Picasso.with(context); } @Override public RequestCreator load(String url) { return picasso.load(url); } }

现在,在您需要 Picasso 的组件中,请改用 ImageLoader

因此,您的方法如下:

public static RequestCreator requestCreatorFromUrl(String picUrl) { return imageLoader.load(picUrl); }

那么您的测试将如下所示:

@Test public void test() { ImageLoaderImpl imageLoader = Mockito.mock(ImageLoaderImpl.class); RequestCreator expected = Mockito.mock(RequestCreator.class); String TEST_URL = "https://www.some.url/img.jpg"; when(imageLoader.load(TEST_URL)).thenReturn(expexted); RequestCreator actual = clazzToTest.requestCreatorFromUrl(TEST_URL); assertEquals(expected, actual); }

不需要模拟static 方法,不需要PowerMock

【讨论】:

    【解决方案2】:

    来自Mockito's FAQ

    Mockito 有什么限制

    ...

    不能模拟静态方法

    请改用PowerMockHere 你会找到关于如何模拟静态方法的详细说明。

    更新

    为了将 PowerMock 应用到您的测试中,您需要:

    • 在测试类级别添加@PrepareForTest

      @PrepareForTest(Picasso.class) public class ValidationTest { ... }

    • 调用PowerMockito.mockStatic() 模拟一个静态类

      PowerMockito.mockStatic(Picasso.class);

    • 只需使用Mockito.when() 设置您的期望:

      Mockito.when(Picasso.with(mCtx)).thenReturn(requestCreator);

    同样的步骤也适用于RequestCreator.class

    附:我可能会出错,因为我不知道您使用的 3rd 方库的 API。

    【讨论】:

    • 可以使用 PowerMockito。当然,这意味着使用过时版本的 Mockito。
    • 您能否提供一个提示如何使用 powermockito 来测试我的方法?因为 Picasso 类来自我正在使用的外部库..我没有它的代码,我只是将 jar 添加到 gradle.build
    • @GhostCat,我从未听说过 PowerMockito 框架。你的意思是org.powermock.api.mockito.PowerMockito
    • 是的,但更好的链接是:github.com/powermock/powermock/wiki/Mockito
    • @mxf 我需要你的帮助。我做了以下事情,但我对 RequestCreator 有疑问,因为 Picasso.with(mCtx) 返回 RequestCreator..我应该如何测试 .load 方法?我的代码:@Test public void whenRequestCreatorFromUrlTest() throws Exception { PowerMockito.mockStatic(Picasso.class); Uri mockUri = mock(Uri.class); Mockito.when(Picasso.with(mCtx).load(mockUri)).thenReturn(RequestCreator.class) }
    猜你喜欢
    • 1970-01-01
    • 2014-02-08
    • 2019-10-29
    • 1970-01-01
    • 2023-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多