替换实例化类的实现而不触及代码(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本身的实际实现,也无法轻松地为不同的测试场景提供替代实现。