4

我有不想接触的遗留代码。

public class LegacyCode{
    public LegacyCode() {
        Service s = new ClassA();
        s.getMessage();
    }
}

WhereClassA提供 CORBA 服务调用。

public class ClassA implements Service{
    @Override
    public String getMessage() {
       // Make CORBA service call...
       return "Class A";
    }
}

界面Service看起来像;

public interface Service {
    String getMessage();
}

出于测试目的,我想用存根替换Service(在LegacyCode实现中)的实现。ClassA

public class ClassB implements Service {
    @Override
    public String getMessage() {
        return "Stub Class B";
    }
}

到目前为止,一切都很好。但是是否可以在不修改显示的遗留代码ClassB而不是ClassA实例化的情况下加载ClassA

// In my test workbench
new LegacyCode(); // "Stub Class B"

我尝试编写一个自定义类加载器并在应用程序启动时通过 java vm 参数加载它,但该加载器仅LegacyCode加载了第一个类(此处)。

提前致谢

4

3 回答 3

3

使用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构造函数时注入您的存根,而无需更改遗留代码。希望这就是你所需要的。

于 2013-10-11T00:06:39.433 回答
1

一种方法是使用 AspectJ。我同意 Hovercraft 的观点,这是依赖注入的一个很好的例子,但是如果你不能更改源代码,AspectJ 可能是你选择的工具。

这比我更好地解释了 AspectJ 案例:AspectJ 可以在第三方库代码中将“new X”替换为“new SubclassOfX”吗?

于 2013-10-10T22:21:42.957 回答
0

更简单的做法是ClassA在您的测试代码中创建一个。在实际代码中最初找到的同一个包中声明它,并使用相同的方法名称。只是让方法什么都不做。

例如,如果您的项目结构如下所示:

+- src
  +- main
    +- java
      +- com.company.code
        -- LegacyCode.java
        -- Service.java
      +- com.company.code.service
        -- ClassA.java
  +- test
    +- java
      +- com.company.code
        -- LegacyCodeTest.java

ClassA在 下创建替代方案/src/test/java/com/company/code/

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

当您的单元测试执行时,ClassA将从您的test文件夹中加载。这比使用模拟框架要简单得多,特别是如果您的旧代码很混乱,对依赖注入不友好并且实际ClassA在任何地方都使用。

请记住,这种策略会使您的测试代码更加晦涩难懂,并且您无法测试ClassA自身的实际实现,也无法轻松地为不同的测试场景提供替代实现。

于 2018-06-06T23:08:04.843 回答