7

我有一个 2.3.1 版本的 grails 应用程序和下一个配置BuildConfig.groovy

   dependencies {
        ...
        ..
        .
        test "org.spockframework:spock-grails-support:0.7-groovy-2.0"
    }
    plugins {
        test(":spock:0.7") {
            exclude "spock-grails-support"
        }

我有下一个域类:

class Draft {
    def grailsApplication

    String name
    String subject
    String content

    static constraints = {
        name unique: true, blank: false
        subject blank: false
    }

    static mapping = {
        content type: 'text'
    }
}

我发现这篇文章使用 Grails 2.x 和 Spock 0.7 测试域约束,用一种有趣的方法来测试域类约束。

我有一个 spock 测试:

import spock.lang.Specification

abstract class ConstraintUnitSpec extends Specification {

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

    String getEmail(Boolean valid) {
        valid ? "dexter@miamipd.gov" : "dexterm@m"
    }

    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) {
        println "Draft name: " + obj.name 
        def validated = obj.validate()
        if (error && error != 'valid') {
            assert !validated
            assert obj.errors[field]
            assert error == obj.errors[field]
        } else {
            assert !obj.errors[field]
        }
    }
}

import grails.test.mixin.TestFor
import spock.lang.Unroll

@TestFor(Draft)
class DraftSpec extends ConstraintUnitSpec {
     def setup() {
        mockForConstraintsTests(Draft, [new Draft(name: 'unique')])
     }

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

          then:
          validateConstraints(obj, field, error)

          where:
          error                  | field        | val
          'nullable'             | 'name'       | null
          'nullable'             | 'subject'    | null
          'nullable'             | 'content'    | null
          'unique'               | 'name'       | 'unique'
          'valid'                | 'name'       | 'valid name'
          'valid'                | 'subject'    | 'valid subject'
          'blank'                | 'name'       | ''
          'blank'                | 'subject'    | ''
   }
}

blank测试在两个约束中都失败:

Draft name: null
| Failure:  test draft all constraints subject is blank(DraftSpec)
|  Condition not satisfied:

error == obj.errors[field]
|     |  |   |     ||
blank |  |   |     |subject
      |  |   |     nullable
      |  |   org.codehaus.groovy.grails.plugins.testing.GrailsMockErrors: 3 errors
      |  |   Field error in object 'Draft' on field 'name': rejected value [null]; codes [Draft.name.nullable.error.Draft.name,Draft.name.nullable.error.name,Draft.name.nullable.error.java.lang.String,Draft.name.nullable.error,draft.name.nullable.error.Draft.name,draft.name.nullable.error.name,draft.name.nullable.error.java.lang.String,draft.name.nullable.error,Draft.name.nullable.Draft.name,Draft.name.nullable.name,Draft.name.nullable.java.lang.String,Draft.name.nullable,draft.name.nullable.Draft.name,draft.name.nullable.name,draft.name.nullable.java.lang.String,draft.name.nullable,nullable.Draft.name,nullable.name,nullable.java.lang.String,nullable]; arguments [name,class Draft]; default message [Property [{0}] of class [{1}] cannot be null]
      |  |   Field error in object 'Draft' on field 'subject': rejected value [null]; codes [Draft.subject.nullable.error.Draft.subject,Draft.subject.nullable.error.subject,Draft.subject.nullable.error.java.lang.String,Draft.subject.nullable.error,draft.subject.nullable.error.Draft.subject,draft.subject.nullable.error.subject,draft.subject.nullable.error.java.lang.String,draft.subject.nullable.error,Draft.subject.nullable.Draft.subject,Draft.subject.nullable.subject,Draft.subject.nullable.java.lang.String,Draft.subject.nullable,draft.subject.nullable.Draft.subject,draft.subject.nullable.subject,draft.subject.nullable.java.lang.String,draft.subject.nullable,nullable.Draft.subject,nullable.subject,nullable.java.lang.String,nullable]; arguments [subject,class Draft]; default message [Property [{0}] of class [{1}] cannot be null]
      |  |   Field error in object 'Draft' on field 'content': rejected value [null]; codes [Draft.content.nullable.error.Draft.content,Draft.content.nullable.error.content,Draft.content.nullable.error.java.lang.String,Draft.content.nullable.error,draft.content.nullable.error.Draft.content,draft.content.nullable.error.content,draft.content.nullable.error.java.lang.String,draft.content.nullable.error,Draft.content.nullable.Draft.content,Draft.content.nullable.content,Draft.content.nullable.java.lang.String,Draft.content.nullable,draft.content.nullable.Draft.content,draft.content.nullable.content,draft.content.nullable.java.lang.String,draft.content.nullable,nullable.Draft.content,nullable.content,nullable.java.lang.String,nullable]; arguments [content,class Draft]; default message [Property [{0}] of class [{1}] cannot be null]
      |  Draft : (unsaved)
      false
      6 differences (25% similarity)
      (b-)l(-)a(nk-)
      (nu)l(l)a(ble)

    at ConstraintUnitSpec.validateConstraints(ConstraintUnitSpec.groovy:29)
    at DraftSpec.test draft all constraints(DraftSpec.groovy:18)
| Completed 8 spock tests, 2 failed in 0m 6s
| Tests FAILED  - view reports in /Users/sdelamo/Documents/Developer/bitbucket/concertados-webapp/target/test-reports

问题

Draft当我作为值传递时,的name字段设置为 null''

我错过了什么?提前致谢。

简单测试也失败了

def "test blank constraints"() {
          when: 'the name and subjects are blank'
          def d = new Draft(name: '', subject:'')

          then: 'the validation should fail with blank errors'
          !d.validate()
          'blank' == d.errors["name"]
          'blank' == d.errors["subject"]
   }

这是错误:

| Running 1 spock test... 1 of 1
--Output from test blank constraints--
| Error --Output from test blank constraints--
| Failure:  test blank constraints(com.softamo.concertados.DraftSpec)
|  Condition not satisfied:

'blank' == d.errors["name"]
        |  | |     |
        |  | |     nullable
        |  | org.codehaus.groovy.grails.plugins.testing.GrailsMockErrors: 3 errors
4

2 回答 2

7

Time_yates指出了这个问题:

http://grails.org/doc/latest/ref/Constraints/nullable.html

对于没有值的输入字段,表单提交产生的​​ Web 请求将具有空白字符串,而不是 null。在对不可为空的属性进行批量属性绑定时,请记住这一点。默认行为是空白字符串不会验证 nullable: false 因为数据绑定器会将空白字符串转换为 null。这包括空字符串和空字符串。空白字符串是任何字符串,因此 trim() 方法返回一个空字符串。要关闭空字符串到 null 的转换,请将 Config.groovy 中的 grails.databinding.convertEmptyStringsToNull 属性设置为 false。有关数据绑定的更多详细信息,请参阅数据绑定部分。

于 2013-11-08T09:14:52.123 回答
0

对于 Grails 2.3.9,这个测试突出了空白约束的问题,即使使用上面提到的配置设置(在 then 语句中检查)。

一直没能找到问题。BlankConstraint 不检查 null 属性,并且空白和可为空的约束都是可否决的,所以如果一个失败,所有其他的都将被忽略。

待测类:

package test

class TestVetoableConstraints {
    String nullableAndBlank
    String nullableAndNotBlank
    String notNullableAndBlank
    String notNullableAndNotBlank

    static constraints = {
    nullableAndBlank(nullable:true, blank:true)
    nullableAndNotBlank(nullable:true, blank:false)
    notNullableAndBlank(nullable:false, blank:true)
    notNullableAndNotBlank(nullable:false, blank:false)
    }
}

测试:

package test

import grails.test.mixin.TestFor
import spock.lang.Specification
import grails.util.Holders

@TestFor(TestVetoableConstraints)
class TestVetoableConstraintsSpec extends Specification {
    def config = Holders.config

    void "test nullable and blank constraints" (error, field, val) {
        when: 
            mockForConstraintsTests(TestVetoableConstraints)
            def obj = new TestVetoableConstraints("$field": val)

        then: 
// These are set to ensure that an empty string isn't trimmed and converted to null
            false == config.grails.databinding.convertEmptyStringsToNull
            false == config.grails.databinding.trimStrings
            validateConstraints(obj, field, error)

        where:
            error                  | field                      | val
            'valid'                | 'nullableAndBlank'         | null
            'valid'                | 'nullableAndBlank'         | ''
            'valid'                | 'nullableAndBlank'         | 'Good String'
            'valid'                | 'nullableAndNotBlank'      | null
// The next test should return blank, but actually fails on "assert obj.errors[field]" which is null
            'blank'                | 'nullableAndNotBlank'      | ''
            'valid'                | 'nullableAndNotBlank'      | 'Good String'
            'nullable'             | 'notNullableAndBlank'      | null
// The next test should be valid, but actually fails on "assert !obj.errors[field]" which returns 'nullable'
            'valid'                | 'notNullableAndBlank'      | ''
            'valid'                | 'notNullableAndBlank'      | 'Good String'
            'nullable'             | 'notNullableAndNotBlank'   | null
// The next test should return blank, but actually fails on "assert error == obj.errors[field]" because it returns 'nullable'
            'blank'                | 'notNullableAndNotBlank'   | ''
            'valid'                | 'notNullableAndNotBlank'   | 'Good String'
    }

    private 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]
       }
   }
}
于 2014-06-12T15:26:00.857 回答