Grails域标准验证器:我应该测试吗?

pro*_*ski 2 testing tdd grails unit-testing

这是grails应用程序中的一个简单域类:

class User {
    String username

    static constraints = {
        username unique: true
    }
}
Run Code Online (Sandbox Code Playgroud)

我的问题是:我应该编写单元测试来检查用户名字段是否唯一?

@Test
void cannotCreateMoreThanOneUserWithTheSameUsername() {
    new User(username: 'john').save()

    def secondUser = new User(username: 'john')
    assert !secondUser.validate()
}
Run Code Online (Sandbox Code Playgroud)

我有点怀疑,因为:

  • 如果我按照TDD原则编写User类,那么我应该在实现约束闭包之前编写失败的测试.

  • 另一方面,在域中设置唯一约束是数据模型配置而不是真实逻辑.而且,保存和验证方法在框架中实现.

Gre*_*egg 6

在我看来,单元测试CRUD方法并不值得,因为Grails开发人员已经对这些方法进行了全面测试.另一方面,单元测试约束很重要,因为约束可能会在应用程序的生命周期中发生变化,并且您希望确保捕获这些更改.您永远不知道可能需要修改哪些业务逻辑来支持所述更改.我喜欢使用Spock,典型的约束测试看起来像这样:

@TestFor(User)
class UserSpec extends ConstraintUnitSpec {

  def setup() {
    mockForConstraintsTests(User, [new User(username: 'username', emailAddress: 'email@email.com')])
  }

  @Unroll("test user all constraints #field is #error")
  def "test user all constraints"() {
    when:
    def obj = new User("$field": val)

    then:
    validateConstraints(obj, field, error)

    where:
    error      | field                 | val
    'blank'    | 'username'            | ' '
    'nullable' | 'username'            | null
    'unique'   | 'username'            | 'username'
    'blank'    | 'password'            | ' '
    'nullable' | 'password'            | null
    'maxSize'  | 'password'            | getLongString(65)
    'email'    | 'emailAddress'        | getEmail(false)
    'unique'   | 'emailAddress'        | 'email@email.com'
    'blank'    | 'firstName'           | ' '
    'nullable' | 'firstName'           | null
    'maxSize'  | 'firstName'           | getLongString(51)
    'blank'    | 'lastName'            | ' '
    'nullable' | 'lastName'            | null
    'maxSize'  | 'lastName'            | getLongString(151)
    'nullable' | 'certificationStatus' | null
  }
}
Run Code Online (Sandbox Code Playgroud)

这是ConstraintUnitSpec基类:

abstract class ConstraintUnitSpec extends Specification {

  String getLongString(Integer length) {
    'a' * length
  }

  String getEmail(Boolean valid) {
    valid ? "test@wbr.com" : "test@w"
  }

  String getUrl(Boolean valid) {
    valid ? "http://www.google.com" : "http:/ww.helloworld.com"
  }

  String getCreditCard(Boolean valid) {
    valid ? "4111111111111111" : "41014"
  }

  void validateConstraints(obj, field, error) {


    def validated = obj.validate()

    if (error && error != 'valid') {
      assert !validated
      assert obj.errors[field]
      assert error == obj.errors[field]
    } else {
      assert !obj.errors[field]
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

这是我从博客文章中学到的一种技巧.但我现在不记得了.我会寻找它,如果我找到它,我会确定并链接到它.