替换实例化类的实现而不触及代码(java)
我有遗留的代码我不想碰。
public class LegacyCode{ public LegacyCode() { Service s = new ClassA(); s.getMessage(); } }
ClassA
提供CORBA服务调用的地方。
public class ClassA implements Service{ @Override public String getMessage() { // Make CORBA service call... return "Class A"; } }
界面Service
看起来像;
public interface Service { String getMessage(); }
出于测试目的,我想用存根替换Service
的实现(在ClassA
实现的LegacyCode
)。
public class ClassB implements Service { @Override public String getMessage() { return "Stub Class B"; } }
到现在为止还挺好。 但是,如果没有对显示的遗留代码进行任何修改,是否可以在ClassA
实例化时加载ClassB
而不是ClassA
?
// In my test workbench new LegacyCode(); // "Stub Class B"
我已经尝试编写一个自定义类加载器并在应用程序启动时通过java vm参数加载它,但只有第一个类(此处为LegacyCode
)由该加载器加载。
提前致谢
使用PowerMock
您可以为构造函数代码创建模拟(或存根)。 答案来自这个链接 。 我会尝试将其转换为与您的用例完全匹配:
@RunWith(PowerMockRunner.class) @PrepareForTest(ClassA.class) public class LegacyTester { @Test public void testService() { // Inject your stub PowerMock.createMock(ClassA.class); Service stub = new MyServiceStub(); PowerMock.expectNew(ClassA.class).andReturn(stub); PowerMock.replay(stub, ClassA.class); // Implement test logic here LegacyCode legacyCode = new LegacyCode(); // Implement Test steps // Call verify if you want to make sure the ClassA constructor was called PowerMock.verify(stub, ClassA.class) } }
这样就可以在调用ClassA
构造函数时注入存根,而无需更改遗留代码。 希望这就是你所需要的。
一种方法是使用AspectJ。 我同意Hovercraft,这是dependency injection的一个很好的例子,但如果你不能改变源代码,AspectJ可能是你的首选工具。
这比我更好地解释了AspectJ案例: AspectJ可以在第三方库代码中用“new SubclassOfX”替换“new X”吗?
更简单的方法是在测试代码中创建一个ClassA
。 在最初在实际代码中找到的相同包中声明它并使用相同的方法名称。 只是让方法什么都不做。
例如,如果您的项目结构如下所示:
+- src +- main +- java +- com.company.code -- LegacyCode.java -- Service.java +- com.company.code.service -- ClassA.java +- test +- java +- com.company.code -- LegacyCodeTest.java
在/src/test/java/com/company/code/
下创建另一个ClassA
:
package com.company.code.service; /** Replaces the original implementation for testing purposes */ public class ClassA implements Service{ @Override public String getMessage() { return "I replaced the original implementation"; } }
现在您的项目结构将如下所示:
+- src +- main +- java +- com.company.code -- LegacyCode.java -- Service.java +- com.company.code.service -- ClassA.java +- test +- java +- com.company.code -- LegacyCodeTest.java +- com.company.code.service -- ClassA.java
执行unit testing时,将加载test
文件夹中的ClassA
。 这比使用模拟框架更简单,特别是如果您的旧代码很乱,而不是dependency injection友好且实际的ClassA
在任何地方都使用。
请记住,这种策略可以使您的测试代码更加模糊,并且您无法测试ClassA
本身的实际实现,也无法轻松地为不同的测试场景提供替代实现。