运行 针对两个不同的注入依赖项进行单元测试

Run unit tests against two different injected dependencies

我的应用程序中有两个服务 bean,它们都实现了一个接口。对于该接口,所有方法都必须执行相同的操作(内部结构不同)。

所以我想编写一组针对这两种服务运行的测试。 (不想写重复代码)

构建我的测试以实现此目的的最佳方式是什么?

(我标记了 junit4,因为这是我目前限制的版本。)

您可以在 JavaConfig 中创建一个包含实现列表的 bean:

public class TestConfig {

    @Bean
    MyService myService1(){
        return new MyService1();
    }

    @Bean
    MyService myService2(){
        return new MyService2();
    }

    @Bean
    public List<MyService> myServices(MyService myService1, MyService myService2){
        List<MyService> allServices = new ArrayList<>();
        allServices.add(myService1);
        allServices.add(myService2);
        return allServices;
    }
}

并最终在您的测试中迭代此列表:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=TestConfig.class)
public class ServicesTest {
    @Autowired
    private List<MyService> allServices;
    @Test
    public void testAllServices(){    
        for (MyService service : allServices) {
            // Test service here
        }    
    }
}

几个选项:

  1. 您可以将所有 classes 存储在常量中,例如 List<Class<? extends YourInterface>> classes = Arrays.asList(Implementation.class) 循环遍历这些 classes 并为每个

    [调用方法=20=]
  2. 您可以使用 Reflections 找到所有实现特定接口的 classes 并为每个 class.

  3. 循环

您可以有一个自动装配的服务列表,并按照某些人的建议循环遍历并测试每个实现。但我想说这实际上不是一个很好的解决方案,原因有很多:

  • 如果任何一个实现失败,那么另一个实现可能被破坏,你不会知道,直到你 运行 再次测试并修复错误
  • 最好进行更多非常简单的测试,而不是进行更多的测试,因为这样会更难追踪故障。
  • 需要 Spring - 这一点可能无关紧要,但如果至少减少正在测试的系统数量,不依赖所有这些结构可能是有利的

您可能会喜欢类型参数并从通用基础 class 继承,例如 class BaseTest<HW extends HelloWorldInterface >@Autowired HW hw,然后简单地使用 class 扩展此 class =13=] 其中 GutenTag implements HelloWorldInterface.

但是我试过了,我发现简单地针对接口编写断言函数更简单,如下所示:

public static void assertImplementationSaysHelloWorld(HelloWorldInterface hello, String expectedResult) {
    assertEquals(expectedResult, hello);
}

发生的事情少得多,而且该功能比继承更可重用。例如,如果我有一个 class 实现了多个接口,使用继承方法我将被迫编写更多 classes 来测试每个接口。而我可以在一次测试中使用任意多个不同的断言函数 class.

这里的其他答案都很好,但不完全符合我的喜好。 我最终结合了

  • JUnit 的 @RunWith(@Parameterized)(运行 两种服务的测试)
  • Spring 的新规则(保留 @RunWith(SpringRunner.class) 容器、appcontext、webmvc 和注入功能)

以下是片段:

@RunWith(Parameterized.class)

...

@Parameters(name = "Cached = {0}")
public static Boolean[] data() {
    return new Boolean[] { Boolean.TRUE, Boolean.FALSE };
}

@ClassRule
public static final SpringClassRule springClassRule = new SpringClassRule();

@Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();

@Before
public void setUp() {
     // logic to choose the injected service based on the true/false param
}