【问题标题】:Writing testable codes - Junit Test编写可测试的代码 - Junit Test
【发布时间】:2019-12-30 06:39:07
【问题描述】:

目前正在进行一个项目,该项目需要我为我已实现的所有代码编写单元测试。 我是编写单元测试的新手,最近学习了使用 @MockBeans@InjectMocks@Spy 等注释编写测试用例的知识,以帮助我测试特定的方法。

但是,有时我确实在编写无法模拟的测试用例时遇到困难,最终成为集成测试。以下是我在编写测试用例时遇到的一些场景。

  1. 服务层的单元测试最终成为一个集成测试,因为它与 repo 层交互。

    • repo 层将 Tuple (javax.persistence.tuple) 列表返回给服务层。
    • 模拟了 repo 层,没有问题。
    • 尝试创建当通过 Mockito.when() 调用 repo 层时应该返回的元组列表,但是,我无法创建元组列表,因为它没有任何 setter 方法。
    • 以单元测试结束,对数据库执行实际查询以检索出值,这使得单元测试更像是集成测试。
  2. 其中涉及工厂方法调用的方法。工厂类被编写为静态类,然后返回的具体类使用 AutowireCapableBeanFactory 自动装配到 Spring 上下文中

    • 由于无法模拟静态工厂,我只需将值传入工厂并让它相应地返回具体类。
    • 无法模拟工厂返回的具体类。
    • 单元测试最终也会测试具体类。
  3. 使用new ConcreteClass() 实例化新类的方法 而不是@Autowired。

    • 我认为这类似于我上面提到的第二种情况。
    • @MockBeans 在这里不起作用,因为具体类不是使用 @Autowired 创建的。

这些是我目前面临的情况,我花了很多时间试图弄清楚编写测试用例的正确方法是什么,或者更确切地说,我应该如何设计我的代码可以避免所有这些问题吗?感谢您对这些场景的反馈和帮助,以改进我编写单元测试或代码设计的方式,使其更具可测试性!

【问题讨论】:

    标签: java spring unit-testing testing junit


    【解决方案1】:
    1. javax.persistence.Tuple 是一个接口您可以在您的测试代码中创建匿名类,返回适合您的测试用例的值。

      您还可以使用 @Mock 注释创建 mocks 并使用 Mockitos when().thenReturn() 配置对其进行配置。

    2. 无法模拟工厂返回的具体类。

      为什么?

    3. 使用 new ConcreteClass() 而不是 @Autowired 实例化新类的方法。

      a) 为什么你不能改变它?

      b) 将实例化具体类的行提取到具有 package private 可见性的单独方法中。然后从这个被测类中创建一个spy,并将提取的方法配置为返回ConcreteClass的模拟。


    1. 工厂是一个静态类,我如何模拟该工厂以返回我模拟的具体类? 3.会尝试你的建议! – 刚开始

    我会写一个非静态的委托类。这个类太简单了,不会失败,所以它不需要 UnitTests。

    class FactoryWrapper{
      ProductType createProduct(/* some parameters*/) {
        return StaticFactory.createProduct(/* some parameters*/);
      }
    }
    

    请记住,UnitTests 不是验证代码,而是期望的行为。因此,UnitTests 的目标是不是 100% 的代码覆盖率(而是 100% 的需求覆盖率)。

    可以将这个包装器的一个实例注入到 CUT(被测代码)中,并在那时用模拟代替测试。

    作为注入的(更糟糕的)替代方法,package private getter 方法技术可用于访问 Wrapper 实例,如第 3 点所述。

    【讨论】:

    • 感谢您的回复! 1. 没想到,试试看。 2.工厂是一个静态类,我如何模拟那个工厂来返回我模拟的具体类? 3.会尝试你的建议!
    【解决方案2】:
    1. 您可以使用PowerMockito 模拟新对象的创建。

      ConcreteClass concreteObject = mock(ConcreteClass.class);
      PowerMockito.whenNew(ConcreteClass.class)
          .withAnyArguments().thenReturn(concreteObject);
      
    2. 反射可用于模拟工厂类。

    【讨论】:

    • PowerMock 是对糟糕设计的投降。仅当无法选择重构时,才应将其用作最后的手段。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-24
    • 1970-01-01
    • 1970-01-01
    • 2013-01-14
    • 1970-01-01
    相关资源
    最近更新 更多