我有一个版本为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'
}
}
我发现这篇文章Testing Domain Constraints Using 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
的{{1}}字段设置为空
SIMPLE TEST也失败了
''
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"]
}
答案 0 :(得分:7)
Time_yates指出了这个问题:
http://grails.org/doc/latest/ref/Constraints/nullable.html
表单提交产生的Web请求将包含空字符串, 对于没有值的输入字段,不为null。请记住这一点 将mass属性绑定到不可为空的属性。该 默认行为是空字符串不会验证 nullable:false,因为数据绑定器会将空字符串转换为 空值。这包括空字符串和空字符串。一个空白的字符串是 任何字符串,以使trim()方法返回一个空字符串。至 关闭空字符串的转换为null设置 grails.databinding.convertEmptyStringsToNull属性为false Config.groovy中。有关数据的更多详细信息,请参阅数据绑定部分 结合。
答案 1 :(得分:0)
对于Grails 2.3.9,此测试突出显示空白约束的问题,即使使用上述配置设置(在then语句中检查)。
无法追查问题。 BlankConstraint不检查null属性,并且blank和nullable约束都是可否决的,因此如果一个失败,则忽略所有其他约束。
正在测试的课程:
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]
}
}
}