即使您不喜欢它,有时您的工作也需要您维护旧版应用程序。 它发生在我身上(幸运的是很少),在遗留代码库中编写单个字符之前,我首先要寻找的是单元测试工具。 大多数时候,我倾向于专注于我需要更改的应用程序部分的代码覆盖率,并尝试尽可能地对其进行改进,即使完全没有单元测试也是如此。
当设计不是最新技术时,就会发生真正的麻烦。 单元测试需要模拟,这仅在依赖项注入(以及初始化方法)使类可以进行单元测试时才是好方法:对于某些遗留应用程序,情况并非如此,在这些应用程序中,一堆私有和静态方法或初始化代码构造函数,更不用说静态块了。
例如,考虑以下示例:
publicclassExampleUtils{
publicstaticvoiddoSomethingUseful(){...}
}
publicclassCallingCode{
publicvoidcodeThatShouldBeTestedInIsolation(){
ExampleUtils.doSomethingUseful();
...
}
}
无法正确地对codeThatShouldBeTestedInIsolation()
方法进行单元测试,因为它依赖于另一个类的另一个不可模拟的静态方法。 当然,有成熟的技术可以克服这一障碍。 一种这样的技术是创建一个“代理”类,该类将对静态方法的调用包装起来,并将该类注入调用类中,如下所示:
publicclassUsefulProxy{
publicvoiddoSomethingUsefulByProxy(){
ExampleUtils.doSomethingUseful();
}
}
publicclassCallingCodeImproved{
privateUsefulProxyproxy;
publicvoidcodeThatShouldBeTestedInIsolation(){
proxy.doSomethingUSeful();
...
}
}
现在,我可以注入一个模拟的UsefulProxy
并最终UsefulProxy
测试我的方法。 但是,有几个缺点需要考虑:
- 产生的代码没有提供测试,只是提供了使测试成为可能的一种方法。
- 在编写此小替代方法时,您没有进行任何测试。 在这一点上,您什么都没做。
- 您在测试之前更改了代码,并冒着破坏行为的风险! 当然,该示例并不意味着任何复杂性,但在现实生活中并非总是如此。
- 您使代码更具可测试性,但又增加了一层复杂性。
由于所有这些原因,我只建议将此方法作为最后的手段。 更糟糕的是,有些设计完全不支持简单的重构,例如以下显示静态初始化程序的示例:
publicclassClassWithStaticInitializer{
static{...}
}
一旦类加载器加载了ClassWithStaticInitializer
类,就将执行静态块,无论是好是坏(根据单元测试,很可能是后者)。
我选择的模拟框架是Mockito 。 它的设计人员确保没有静态方法模拟之类的功能,对此我深表感谢。 这意味着如果我不能使用Mockito,那是设计的味道。 不幸的是,正如我们之前所见,处理遗留代码可能需要这些功能。 那是进入PowerMock的时候(只有那时-在标准的开发过程中使用PowerMock也是确定设计可疑的标志)。
使用PowerMock,您可以保持原始代码不变,并且仍然可以进行测试以放心地开始更改代码。 这是使用Mockito和TestNG的第一个旧代码段的测试代码:
@PrepareForTest(ExampleUtils.class)
publicclassCallingCodeTest{
privateCallingCodecallingCode;
@BeforeMethod
protectedvoidsetUp(){
mockStatic(ExampleUtils.class);
callingCode=newCallingCode();
}
@ObjectFactory
publicIObjectFactorygetObjectFactory(){
returnnewPowerMockObjectFactory();
}
@Test
publicvoidcallingMethodShouldntRaiseException(){
callingCode.codeThatShouldBeTestedInIsolation();
assertEquals(getInternalState(callingCode,"i"),1);
}
}
没有什么可做的,即:
- 使用
@PrepareForTest
注释测试类(或单个测试方法),@PrepareForTest
引用类或整个程序包。 这告诉PowerMock允许对这些类进行字节码操作,有效指令将在下一步中完成。 - 使用可用的
mockXXX()
方法面板模拟所需的方法。 - 在返回
IObjectFactory
并用@ObjectFactory
注释的方法中提供对象工厂。
还要注意,借助Whitebox
类,我们可以访问类的内部状态( 即私有变量)。 即使这很糟糕,也可以选择-在没有测试工具的情况下使用遗留代码的机会更糟:记住我们的目标是减少引入新错误的机会。
您可以在此处以Maven格式找到本文的资源。
翻译自: https://blog.frankel.ch/powermock-features-and-use-cases/