静态路由_在Android中模拟静态方法:让我们总结一下

静态路由

静态路由

在Android中编写本地单元测试时,面临的局限性之一是测试是针对没有任何代码的android.jar版本运行的。 如文档所述,必须模拟对Android代码的任何依赖关系。

一个简单的单元测试的快速示例:

public class ClassUnderTest {

public String methodUnderTest(String str)
{
    if (PhoneNumberUtils.isGlobalPhoneNumber(str))
    {
      return "yes";
    }
    else
    {
      return "no";
    }
  }
}

@RunWith(JUnit4.class)
public class TestThatFails {

private ClassUnderTest classUnderTest;

  @Before
  public void setup() {
    classUnderTest = new ClassUnderTest();
  }

  @Test
  public void testTheClass() {
    String result = classUnderTest.methodUnderTest("1234");
    assertEquals("yes", result);
  }
}

运行此测试时,它将失败并显示以下错误:

java.lang.RuntimeException: Method isGlobalPhoneNumber in android.telephony.PhoneNumberUtils not mocked. See http://g.co/androidstudio/not-mocked for details

我们正在测试的类依赖于Android实用工具库PhoneNumberUtils 。 为了使测试成功运行,必须模拟此Android依赖项。

这篇文章的所有示例代码都可以在gist上找到

Mockito:否静态方法

Google建议的模拟Android依赖关系的方法是使用Mockito 。 通常,这很好,但是在我们的示例中,由于Mockito不支持模拟静态方法,因此无法正常工作。

讨论表明,出于各种原因,Mockito贡献者将静态方法视为反模式,例如

  • 在代码中,对静态方法的依赖变得很硬。
  • 这使得模拟和测试变得困难。

因此,他们不支持它,因为他们不想鼓励不良的设计。

那么还有什么其他方法可以使我们的测试工作呢?

  • 如果这是普通的Java而不是Android,那么我可以使用PowerMockito模拟静态方法。 但是我发现在Android中使用PowerMock存在问题。
  • 如果仅使用一些静态方法,则可以在假定源可用的情况下将代码复制到应用程序中。 当然,这意味着需要维护更多的代码,并且如果您使用许多静态方法,这将是不可持续的。
  • 您可以包装静态方法调用并在内部委托给静态方法。 然后可以嘲笑包装器。 这是我们将要讨论的选项。

包装类

一种解决方案是为具有静态方法的Android类创建包装器类,并将该包装器添加为依赖项。

public class PhoneNumberUtilsWrapper {

  public boolean isGlobalPhoneNumber(String phoneNumber)
  {
    return PhoneNumberUtils.isGlobalPhoneNumber(phoneNumber);
  }
}

public class ClassUnderTestWithWrapper {

  private PhoneNumberUtilsWrapper wrapper;

  public ClassUnderTestWithWrapper(PhoneNumberUtilsWrapper wrapper) {
    this.wrapper = wrapper;
  }

  public String methodUnderTest(String str)
  {
    if (wrapper.isGlobalPhoneNumber(str))
    {
      return "yes";
    }
    else
    {
      return "no";
    }
  }
}

在这里,我为PhoneNumberUtils创建了一个包装类,该包装类现在是被测试类的依赖项。

@RunWith(JUnit4.class)
public class TestWithWrapper {

  @Mock
  PhoneNumberUtilsWrapper wrapper;

  private ClassUnderTestWithWrapper classUnderTest;

  @Before
  public void setup() {
    MockitoAnnotations.initMocks(this);

    classUnderTest = new ClassUnderTestWithWrapper(wrapper);
  }

  @Test
  public void testTheClass() {
    when(wrapper.isGlobalPhoneNumber(anyString())).thenReturn(true);

    String result = classUnderTest.methodUnderTest("1234");
    assertEquals("yes", result);
  }
}

因为可以模拟包装器类,并且被测类中的方法调用不是静态的,所以现在可以通过测试。

这种解决方案的一个问题是,当被测类依赖于许多Android库中的静态方法时。 例如,如果被测类也需要使用TextUtilsDateUtils等,会发生什么。突然之间,您最终会得到更多的样板代码,更多的构造函数参数等。

包装方法

另一种方法是将静态方法调用包装在被测类的非静态方法中。

public class ClassUnderTestWithWrappedMethod {

  public String methodUnderTest(String str)
  {
    if (isGlobalPhoneNumber(str))
    {
      return "yes";
    }
    else
    {
      return "no";
    }
  }

  // can't be private access
  boolean isGlobalPhoneNumber(String phoneNumber)
  {
    return PhoneNumberUtils.isGlobalPhoneNumber(phoneNumber);
  }
}

为了使它起作用,在测试中我们必须使用Mockito spy 。 还要注意,包装的方法必须在测试中可以访问,因此不能是私有的。

@RunWith(JUnit4.class)
public class TestWithWrappedMethod {

  private ClassUnderTestWithWrappedMethod classUnderTest;

  private ClassUnderTestWithWrappedMethod classUnderTestSpy;

  @Before
  public void setup() {
    MockitoAnnotations.initMocks(this);

    classUnderTest = new ClassUnderTestWithWrappedMethod();
    classUnderTestSpy = Mockito.spy(classUnderTest);
  }

  @Test
  public void testTheClass() {
    doReturn(true).when(classUnderTestSpy)
    .isGlobalPhoneNumber(anyString());

    String result = classUnderTestSpy.methodUnderTest("1234");
    assertEquals("yes", result);
  }
}

在这里,我们在spy类上运行测试,它将把方法调用委派给被测真实类。 但是,我们可以为将静态方法调用包装到Android库的方法创建存根。

正如我已经提到的那样,一个缺点是包装的方法不能私有,这从OO设计的角度来看不是理想的。 但是如果使用DaggerButterknife之类的库,则必须做出类似的折衷。

结论

这两种包装解决方案都可以使用,但是保持一致并尽可能坚持一种方式可能是个好主意。 哪种方法效果更好可能取决于您的应用程序架构,例如您是否使用依赖项注入。

静态方法:好还是坏? 有关系吗?

在这篇文章中,我不会讨论静态方法是好还是坏的设计(尽管我个人认为应该限制使用它们)。 互联网上已经有很多关于此问题的争论。

但是,在Java世界中,它们已成为现实。

Java,Android和许多流行的库中的许多实用程序类不是真正的OO类,而是过程函数的集合。 这些函数通常被编写为静态方法,并按功能分组。

无论您是否喜欢静态方法,我们都必须学会以务实的方式处理它们。

翻译自: https://www.javacodegeeks.com/2017/12/mocking-static-methods-android-lets-wrap.html

静态路由

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值