我在grails中有一组基本的单元测试,并且我得到一些奇怪的,不一致的行为。所以基本上单元测试是用于与我的用户域交互的服务。
问题处于高水平 - 如果我运行完整的Spec测试 - 每次测试失败4次。如果我单独运行它们 - 它们会通过。这里显而易见的是,测试之间存在状态 - 但鉴于这是一个单元测试 - 不应该(并且我已经通过日志记录验证了这一点)。
这里的组件是:
所以看看Spec我在设置中有以下内容:
User user
def setup() {
println "================ Setting Up User (${User.count()}) ================"
// Set the roles
new Role(authority: "ROLE_ADMIN").save()
new Role(authority: "ROLE_OPERATOR").save()
def role = new Role(authority: "ROLE_USER").save()
def userAddress = new Address()
userAddress.with {
name = "Name"
address1 = "Address 1"
address2 = "Address 2"
townCity = "Town"
postcode = "BT19"
county = "Down"
}
// Create an admin user
user = new User()
user.with {
christianName = "Phil"
surname = "Preston"
email = "p@p.com"
username = "admin"
password = "password123"
phone = ""
skype = ""
address = userAddress
}
println(user.properties)
// Save the test user
if (user.validate()) {
println "Saving"
user.save(flush: true, failOnErrors: true)
UserRole.create(user, role)
}
else {
user.errors.allErrors.eachWithIndex { i, x ->
println "${i} - ${x}"
}
}
assert user
}
两个简单的测试如下:
void "Test updateUser - changing a user password"() {
given: "An update to the user"
UserCommand cmd = new UserCommand()
cmd.with {
id = user.id
christianName = user.christianName
surname = user.surname
username = user.username
email = user.email
skype = user.skype
phone = user.phone
password = "newPassword"
passwordCheck = "newPassword"
isAdmin = user.isAdmin()
isOperator = user.isOperator()
}
when: "attempt to update"
User updated = service.updateUser(cmd)
then: "the password should be update - but nothing else"
updated.password == cmd.password
}
void "Test updateUser - changing a user detail"() {
given: "An update to the user"
UserCommand cmd = new UserCommand()
cmd.with {
id = user.id
christianName = user.christianName
surname = user.surname
username = user.username
email = "update@update.com"
skype = user.skype
phone = user.phone
password = user.password
isAdmin = user.isAdmin()
isOperator = user.isOperator()
}
when: "attempt to update"
User updated = service.updateUser(cmd)
then: "the email should be update - but nothing else"
updated.email == cmd.email
}
(还有其他 - 但这都是为了证明这个问题)
所以奇怪的是:
setup()
中的代码每次打印用户对象数(0)assert
和日志记录)单元测试失败,因为我在用户域对象验证失败时抛出异常。以下是:
def updateUser(UserCommand userCommand) {
assert userCommand
// Get the current
User foundUser = User.findById(userCommand.id)
def wasAdmin = foundUser.admin
def wasOperator = foundUser.operator
// Bind Data
bindData(foundUser, userCommand, ['class', 'operator', 'admin', 'address'])
foundUser?.address?.with {
name = userCommand.name
address1 = userCommand.address1
address2 = userCommand.address2
townCity = userCommand.townCity
county = userCommand.county
postcode = userCommand.postcode
}
// THIS VALIDATION FAILS ON SECOND TEST
if (foundUser.validate()) {
foundUser.save()
// ... removed
return foundUser.refresh()
}
else {
Helper.copyErrorToCmd(foundUser, userCommand)
log.error("Errors: ${foundUser.errors.allErrors}")
throw new UserServiceException(cmd: userCommand, message: "There were errors updating user")
}
用户域对象上的错误基本上(缩短):
所以你可以看到我有这些字段的空字符串 - 并且转换为null(如Grails所做的那样),这就是原因。然而,问题是:
nullable: true
因此,用户域对象中的约束如下:
static constraints = {
christianName blank: false
surname blank: false
username blank: false, unique: true, size: 5..20
password blank: false, password: true, minSize: 8
email email: true, blank: false
phone nullable: true
skype nullable: true
address nullable: true
}
我正在使用具有以下约束的Command对象:
static constraints = {
importFrom User
importFrom Address
password blank: false, password: true, minSize: 8
passwordCheck(blank: false, password: true, validator: { pwd, uco -> return pwd == uco.password })
}
注意我甚至尝试在UserCommand约束关闭中添加Skype和电话字段。
如果我向setup
中的字段添加文字,则此问题就会消失,但实际应用中的字段不可能,因为这些字段可以留空。
非常感谢任何关于如何发生这种不一致的帮助。
康
我添加了一个重新创建问题的最小GitHub项目:Github Project Recreating Issue。要查看问题:
./grailsw test-app
将运行两次测试,第一次传递第二次失败。要单独运行失败的测试:
./gradlew test --tests "org.arkdev.bwmc.accountmission.UserServiceSpec.*Test updateUser - changing a user detail"
你可以看到测试通过。
问题似乎是skype
和phone
字段可以为空:第一次测试为true,第二次测试为nullable:false(我进入user.validate()
,进入GrailsDomainClassValidator.java
,在validate(Object,Errors,boolean)
调用中我可以看到constrainedProperties
地图(基本上是字段 - >约束)。这表明Skype是NullableConstraint.nullable == true
,在第一次调用setup,但是在第二次测试的安装调用中显示NullableConstraint.nullable == false
。)。这毫无意义。
答案 0 :(得分:0)
你不应该在技术上测试框架中的约束条件,但我之前遇到过类似的问题,所以我可以看到你想要的原因。
是否可以发布完整测试?你在使用@ TestFor / @ Mock吗?就像检查一样,但我认为你必须有@Mock,否则你会得到一个不同的错误来创建域类? @Mock是要使用的约束(或@GrailsUnitTestMixin afaik)所必需的。
您在评论中说#34;您应用中的值可以为空白&#34 ;;应该注意的是,这也是可空的。如果它真的可以是空白你应该更改/添加它然后""会工作
你也没有使用' failOnError:true'正如你把它拼错了一样。
抱歉,我没有足够的代表作为评论写作。
答案 1 :(得分:0)
第一次通话后
bindData(foundUser, userCommand, ['class', 'operator', 'admin', 'address'])
UserService.updateUser(UserCommand)
中的(由要素方法Test updateUser - changing a user password
调用)静态成员User.constraints
变为null
。去搞清楚。我不知道Grails是如何工作的,所以也许别人可以理解它。但对我来说,这似乎不应该发生。也许你在某种程度上犯了一个错误。