【问题标题】:Clean tests and Improve test redability to avoid problems between mocks清洁测试并提高测试可重复性以避免模拟之间的问题
【发布时间】:2021-11-10 19:13:49
【问题描述】:

想象一下,我有几个集成测试来验证产品在不同条件下的成功处理。

此产品具有 ID、名称和描述。描述只接受明文,但我也想开始接受一些 HTML 元素 - 不用说我正在考虑 XSS attacks

为了验证我使用的 HTML 标签,JSoup 允许我只定义我想要允许的 HTML 标签。

这将是我将用来模拟产品的代码示例,请记住,对于每种情况,我都需要一个具有不同值的 setDescription(description)

private Product buildProduct() {
    Product product = new Product();
    product.setName("ProductName");
    product.setDescription("<p>Description</p>")
}

也就是说,我将不得不以三种不同的方式模拟产品:

  • 带有描述的产品模型,没有 HTML 标记
  • 带有有效 HTML 标签描述的产品模型
  • 带有无效 HTML 标签描述的产品模型

我想为每种情况创建一个buildProduct(),但这会导致代码重复。 我曾考虑将其拆分为三个不同且独立的测试,但为类似情况创建多个不同的测试似乎没有意义。

要做到这一点,最简洁的方法是什么,请记住,目标不仅是验证测试,还要使测试尽可能简洁易读?

【问题讨论】:

  • 您使用的是 Java 还是 Kotlin?
  • 嗨 João,我正在使用 Java 和 Spring Boot

标签: spring unit-testing testing integration-testing code-cleanup


【解决方案1】:

鉴于您使用的是 Java,因此没有很好的方法可以做到这一点。原因是没有像在 Kotlin 中那样复制对象和更改一个或多个属性的好方法(请查看 here 了解更多详细信息)。

话虽如此,您有两个选择。

第一个是创建一个实际创建所有这些不同对象的实用程序类。是的,代码会被复制,但至少这种丑陋会隐藏在一个非常具体的文件中。

public class TestObjects {
    public static Product productWithDescriptionContainingNoHtmlTags() {
       Product product = new Product();
       product.setName("ProductName");
       product.setDescription("No HTML Tags");
       return product;
    }
   
    public static Product productWithDescriptionContainingValidHtmlTags() {
        Product product = new Product();
        product.setName("ProductName");
        product.setDescription("<p>Description</p>")
        return product;
    }
   
    public static Product productWithDescriptionContainingInvalidHtmlTags() {
        Product product = new Product();
        product.setName("ProductName");
        product.setDescription("<invalid>Description</invalid>")
        return product;
    }
}

然后在你的测试类中,你只需要在合适的地方使用这些方法。

第二个可能是向您的Product 类添加一个构造函数,该类同时接收namedescription

public class Product {

    private String name;
    private String description;

    public Product() { }

    public Product(String name, String description) {
        this.name = name;
        this.description = description;
    }
}

然后在您的测试类中,您可以执行以下操作来创建此类对象:

private Product productWithDescriptionContainingNoHtmlTags = 
      new Product("ProductName", "No HTML Tags");
private Product productWithDescriptionContainingValidHtmlTags = 
      new Product("ProductName", "<p>Description</p>");
private Product productWithDescriptionContainingInvalidHtmlTags = 
      new Product("ProductName", "<invalid>Description</invalid>");

附带说明,当您需要在不创建多个测试的情况下测试一些不同的场景时,一个不错的技巧是使用参数化测试。大致如下:

private static Stream<Arguments> products() {
    return Stream.of(
      Arguments.of(new Product("ProductName", "No HTML Tags"), true),
      Arguments.of(new Product("ProductName", "<p>Description</p>"), true),
      Arguments.of(new Product("ProductName", "<invalid>Description</invalid>"), false)
    );
}

@ParameterizedTest
@MethodSource("products")
void assertProductsValidity(Product product, boolean isValid) {
    // Your test here
}

查看https://www.baeldung.com/parameterized-tests-junit-5#6-method了解更多详情。

【讨论】:

  • 嗨若昂,感谢您的回复!嗯,第一个案例是我的想法,我想让它更干净。我知道参数化测试,并且已经在其他概念中使用过它们。目前我采用了第一种方法,但这个想法将在以后进行重构。我真的很喜欢你制作参数流来启动各种对象的方式,谢谢你的提示!
  • 不客气!如果您愿意,请考虑接受答案;) 或者不接受,并将其留给可能的新/更好的方法。
猜你喜欢
  • 2018-08-06
  • 2012-05-08
  • 2016-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多