技术开发 频道

精通 Grails: 测试 Grails 应用程序

  为简单的验证错误消息编写测试

  下一个用户场景说明 hotel 字段不能留空。这很容易通过内置的 Grails 验证框架来实现。将一个 static constraints 块添加到 HotelStay,如清单 8 所示:

  清单 8. 将一个 static constraints 块添加到 HotelStay

class HotelStay {
  static constraints
= {
    hotel(blank:
false)
    checkIn()
    checkOut()
  }
  
  
String hotel
  
Date checkIn
  
Date checkOut
  
  
//the rest of the class remains the same
}

  输入 grails run-app。如果您尝试在留空 hotel 字段的情况下创建一个 HotelStay,将收到如图 6 所示的错误消息:

  图 6. 空字段的默认错误消息  

  我敢保证您的用户会喜欢这个特性,但对默认的错误消息还不是很满意。假设他们稍微改动了一下用户场景:hotel 字段不能留空;如果留空,错误消息会提示 “Please provide a hotel name”。

  现在您已经添加了一些定制代码 — 尽管它就像一个定制的 String 那么简单 — 接下来应该添加测试了(当然,编写一个验证用户场景的完整性的测试 — 尽管不涉及到定制代码 — 也是完全可以接受的。

  打开 grails-app/i18n/messages.properties 并添加 hotelStay.hotel.blank=Please provide a hotel name。尝试在浏览器中提交一个空 hotel。这时您将看到自己的定制消息,如图 7 所示:

  图 7. 显示定制的验证错误消息  

  向 HotelStayTests.groovy 添加一个新测试,检验对空字段的验证是否有效,如清单 9 所示:

  清单 9. 测试验证错误

class HotelStayTests extends GroovyTestCase {
  void testBlankHotel(){
    def h
= new HotelStay(hotel:"")
    assertFalse
"there should be errors", h.validate()
    assertTrue
"another way to check for errors after you call validate()", h.hasErrors()
  }

  
//the rest of the tests remain unchanged
}

  在生成的控制器中,您已经看到添加到域类中的 save() 方法。在这里,我本来也可以调用 save(),但事实上我并不想把新的类保存到数据库。我只关注验证是否发生。由 validate() 方法来完成这个任务。如果验证失败,则返回 false。如验证成功,则返回 true。

  hasErrors() 是另一个很有价值的测试方法。在调用 save() 或 validate() 之后,hasErrors() 允许您查看验证错误。

  清单 10 是经过扩展的 testBlankHotel(),它引入了其他一些很有用的验证方法:

  清单 10. 验证错误的高级测试

class HotelStayTests extends GroovyTestCase {
  void testBlankHotel(){
   def h
= new HotelStay(hotel:"")
   assertFalse
"there should be errors", h.validate()
   assertTrue
"another way to check for errors after you call validate()", h.hasErrors()  
  
   println
"\nErrors:"
   println h.errors ?:
"no errors found"  
    
   def badField
= h.errors.getFieldError('hotel')
   println "\nBadField:"
   println badField ?:
"hotel wasn't a bad field"
   assertNotNull
"I'm expecting to find an error on the hotel field", badField


   def code
= badField?.codes.find {it == 'hotelStay.hotel.blank'}
   println "\nCode:"
   println code ?:
"the blank hotel code wasn't found"
   assertNotNull
"the blank hotel field should be the culprit", code
  }
}

  确定类没有通过验证之后,您可以调用 getErrors() 方法(在这里,借助 Groovy 简洁的 getter 语法,它被缩略为 errors),返回一个 org.springframework.validation.BeanPropertyBindingResult。就像 GORM 与 Hibernate 相比是一个瘦 Groovy 层一样,Grails 验证只不过是一个简单的 Spring 验证。

  调用 println 的结果不会在命令行上显示,但它们出现在 HTML 报告中,如图 8 所示:

  图 8. 查看测试的 println 输出  

  在 HotelStayTests 报告的右下角单击 System.out 链接。

  清单 10 中给人亲切感觉的 Elvis 操作符(转过脸来 — 看见他向后梳起的发型和那双眼睛吗?)是一个缩略的 Groovy 三元操作符。如果 ?: 左边的对象为 null,将使用右边的值。

  将 hotel 字段更改为 "Holiday Inn" 并重新运行测试。您将在 HTML 报告中看到另一个 Elvis 输出,如图 9 所示:

  图 9. 测试输出中的 Elvis  

  看见 Elvis 之后,不要忘记清空 hotel 字段 — 如果您不希望留下中断的测试的话。

  如果仍然显示关于 checkIn 和 checkOut 的验证错误,您不必担心。就这个测试而言,您完全可以忽略它们。但是这表明您不应该仅测试错误是否出现 — 您应该确保特定的 错误被抛出。

  注意,我没有断言定制错误消息的确切文本。为什么我上一次关注匹配的字符串(测试 toString 的输出时)而这一次没有关注?toString 方法的定制输出便是上一个测试的目的。这一次,我更关心的是确定验证代码的执行,而不是 Grails 是否正确呈现消息。这表明测试更像一门艺术,而不是科学(如果我想验证准确的消息输出,则应该使用 Web 层测试工具,比如 Canoo WebTest 或 ThoughtWorks Selenium)。

0
相关文章