2017-05-01 67 views
0

我想编写单元测试,验证bean验证约束的正确配置。我认为应该对数据集进行测试,以验证许多不同的组合。JUnit - 使用数据集测试bean验证的最佳实践

到目前为止,我做了这个代码:

@RunWith(Parameterized.class) 
public class TagTest { 

    private Validator validator; 

    @Parameterized.Parameter 
    public Pair<Boolean, Tag> tagPair; 

    @Parameterized.Parameters 
    public static Collection<Pair<Boolean, Tag>> testData() { 

     Collection<Pair<Boolean, Tag>> tags = new ArrayList<>(); 

     Tag tag = new Tag(); 
     tag.setId(1L); 
     tag.setVersion(1L); 
     tag.setTitle("Sample"); 
     tags.add(new ImmutablePair<>(true, tag)); 

     // ... more sample data 

     return tags; 
    } 

    @Before 
    public void init() { 
     ValidatorFactory vf = Validation.buildDefaultValidatorFactory(); 
     this.validator = vf.getValidator(); 
    } 

    @Test 
    public void shouldBeValidated() {   
     Set<ConstraintViolation<Tag>> violations = validator.validate(
       tagPair.getRight()); 

     Assert.assertTrue(violations.isEmpty() == tagPair.getLeft()); 
    } 
} 

什么将读取样本数据最好的解决办法?在代码中保存数据对我来说不是很好。我的例子中的标签是扁平物体。如果对象会变得复杂呢?

单元测试是否可以接受,我会进行bean验证的一般测试,它会接受Object(而不是我的情况下的Tag),然后验证我要推入的任何数据?

+0

通过为包含所有成员的'Tag'提供一个构造函数,可以缩短很多事情,因此不需要调用setter方法。你也可以考虑使用JUnitParams作为你的跑步者,因为它比内置的参数化跑步者有更好的功能。 – ngreen

+0

为Tag提供构造函数不是一般的解决方案。那些有很多属性的实体呢? JUnitParams似乎很有前途。 – srnjak

+0

如果您的模型非常复杂以至于无法使用构造函数,那么您的模型可能错了,或者您的测试可能不够狭窄。否则,您将需要一种方法来序列化所有测试输入,并构建可用于连接参数化测试套件的自定义解串器。 – ngreen

回答

0

尽量保持简单。例如。尽量保持测试数据接近测试逻辑。有2个或多或少的好办法达到你需要的东西:

  • TestNG的使用及其@DataProvider
  • 使用JUnit和保持许多测试在一个测试。虽然通常最好的做法是将测试分为不同的测试方法进行验证,但我认为您可以违反此规则。这是因为验证测试​​太多,而且它们非常简单(请参阅下面的示例)。

您还可以转向其他框架,如斯波克提供了最佳的品种数据提供商,但它们通常是动态语言编写(如Groovy),这将带来弊大于利。

当然另一种选择是使用@RunWith(Parameterized.class)。但这是一个非常奇怪的机制,我从来没有见过任何人在现实生活中使用它,因为它有多么不方便。你也可以看看@Theory

下面是JUnit的一个活生生的例子:

@Test public void validationMustPass_forValidExperimentName() { 
    assertExperimentNameValidationPasses("Min boundary", alphanumeric(1)); 
    assertExperimentNameValidationPasses("Max boundary", alphanumeric(20)); 
    assertExperimentNameValidationPasses("With all alphanumerics and allowed special characters", 
      between(2, 19).with(multipleOf("_-")).alphanumeric()); 
    assertExperimentNameValidationPasses("With spaces", between(2, 19).with(oneOf(" ")).alphanumeric()); 
    assertExperimentNameValidationPasses("Only numbers", between(2, 19).numeric()); 
    assertExperimentNameValidationPasses("Only dashes", between(2, 19).string('-')); 
    assertExperimentNameValidationPasses("Only underscores", between(2, 19).string('_')); 
} 

@Test public void mustNotCreateExperimentWithWrongExperimentName() { 
    assertExperimentNameValidationFails(
      "Longer than max allowed length", 
      "Should be between 1 and 20 symbols.", 
      length(21).alphanumeric()); 
    assertExperimentNameValidationFails("Experiment name is white space", 
      "Should be between 1 and 20 symbols.", 
      " "); 
    assertExperimentNameValidationFails("Experiment name is empty", 
      "Should be between 1 and 20 symbols.", 
      ""); 
    assertExperimentNameValidationFails("Experiment name contains not allowed symbols", 
      "Only English letters, numbers, spaces, dashes and underscores are allowed", 
      length(10).with(oneOf(FORBIDDEN_CHARACTERS)).alphanumeric()); 
} 

而这仅仅是为对象的1场。这里是doing this with Spock的一个例子,但正如我所提到的那样 - 通常不值得。这里有一些关于keep data management effective的基本规则。

+0

谢谢你的回答。它并不能真正解决我的问题,但您与文章的链接有非常好的一点。我发现,我需要的关键字是数据驱动的测试。我不想测试每个约束,但如果验证约束正确放在实体上。 – srnjak