二、測試時,在同一事物中的操做,會被回滾。可是配置了 dbCreate: create-drop,Grails 會默認建立表結構,可是 @Rollback 對建立過程無效,即不會回滾建立出來的表。
從 Grails 單元測試開始
在項目 controller 文件夾上右鍵,新建一個 DemoController
package com.rishiqing.demo
class DemoController {
def index() { }
}
grails 會在建立 DemoController 後,爲你在 src/test/groovy/com/rishiqing/demo 路徑下建立一個單元測試類,這是 grails 框架自動完成的操做
package com.rishiqing.demo
import grails.testing.web.controllers.ControllerUnitTest
import spock.lang.Specification
class DemoControllerSpec extends Specification implements ControllerUnitTest<DemoController> {
def setup() {
}
def cleanup() {
}
void "test something"() {
expect:"fix me"
true == false
}
}
在 DemoController 中編寫業務邏輯,並從新編寫 DemoControllerSpec 類完成一個簡單的單元測試。添加接口
package com.rishiqing.demo
class DemoController {
def renderHello () {
render status:200, text :"
hello"
}
}
在 DemoControllerSpec 中添加單元測試
package com.rishiqing.demo
import grails.testing.web.controllers.ControllerUnitTest
import spock.lang.Specification
class DemoControllerSpec extends Specification implements ControllerUnitTest<DemoController> {
void "test renderHello function"() {
when :
controller.renderHello()
then :
status == 200
response.text == "
hello1" // 測試錯誤的結果
}
}
若是測試異常,則 console 會提示測試結果和測試失敗的信息,並生成一個錯誤信息頁面,可使用瀏覽器打開 D:/proj/testDemo/build/reports/tests/test/index.html 位置的錯誤頁面
Testing started at 19:29 ...
"C:\Program Files\Java\jdk1.8.0_102\bin\java.exe" -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -XX:CICompilerCount=3 -Dgrails.full.stacktrace=true -Djline.WindowsTerminal.directConsole=false -Dfile.encoding=UTF-8 -classpath C:\Users\codingR\AppData\Local\Temp\classpath1646263186.jar org.grails.cli.GrailsCli intellij-command-proxy test-app com.rishiqing.demo.DemoControllerSpec -unit -echoOut --plain-output
:compileJava NO-SOURCE
:compileGroovy UP-TO-DATE
:buildProperties UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileTestJava NO-SOURCE
:compileTestGroovy UP-TO-DATE
:processTestResources NO-SOURCE
:testClasses UP-TO-DATE
:testListening for transport dt_socket at address: 8216
Connected to the target VM, address: '127.0.0.1:8216', transport: 'socket'
Condition not satisfied:
response.text == "hello1"
| | |
| | false
| | 1 difference (83% similarity)
| | hello(-)
| | hello(1)
| hello
org.grails.plugins.testing.GrailsMockHttpServletResponse@3bdf09f9
Condition not satisfied:
response.text == "hello1"
| | |
| | false
| | 1 difference (83% similarity)
| | hello(-)
| | hello(1)
| hello
org.grails.plugins.testing.GrailsMockHttpServletResponse@3bdf09f9
at com.rishiqing.demo.DemoControllerSpec.test renderHello function(DemoControllerSpec.groovy:13)
com.rishiqing.demo.DemoControllerSpec > test renderHello function FAILED
org.spockframework.runtime.SpockComparisonFailure at DemoControllerSpec.groovy:13
Disconnected from the target VM, address: '127.0.0.1:8216', transport: 'socket'
1 test completed, 1 failed
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':test'.
> There were failing tests. See the report at: file:///D:/proj/testDemo/build/reports/tests/test/index.html
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
:test FAILED
BUILD FAILED
Total time: 8.358 secs
Tests FAILED |
Test execution failed
Process finished with exit code 1
Grails 集成測試
grails 用命令建立一個集成測試
$ grails create-integration-test [路徑名 + 測試文件名]
建立一個集成測試,集成測試建立好後,在 src/integration-test 目錄下
Microsoft Windows [版本 10.0.17763.503]
(c) 2018 Microsoft Corporation。保留全部權利。
D:\proj\testDemo>grails create-integration-test com.rishiqing.demo.DemoControllerIntegration
| Created src/integration-test/groovy/com/rishiqing/demo/DemoControllerIntegrationSpec.groovy
D:\proj\testDemo>
打開集成測試文件,和剛纔 grails 自動建立的單元測試文件比較,發現只多了 @Integration 和 @Rollback 註解,並取消了單元測試的實現
單元測試文件
package com.rishiqing.demo
import grails.testing.web.controllers.ControllerUnitTest
import spock.lang.Specification
class DemoControllerSpec extends Specification
implements ControllerUnitTest<DemoController> {
def setup() {
}
def cleanup() {
}
void "test something"() {
expect:"fix me"
true == false
}
}
集成測試文件
package com.rishiqing.test
import grails.testing.mixin.integration.Integration
import grails.transaction.*
import spock.lang.Specification
@Integration
@Rollback
class DemoControllerIntegrationSpec extends Specification {
def setup() {
}
def cleanup() {
}
void "test something"() {
expect:"fix me"
true == false
}
}
實際應用
在實際的 web 應用中,單元測試使用較少,更多的是集成測試。
使用單元測試的位置,好比 Util 模塊中,計算日期的方法,可能須要使用到單元測試。
在 web 應用中,因爲須要各個模塊、層、接口之間協同工做,並且須要用到數據庫 I/O 資源,所以實際項目中使用更多的是集成測試。
準備工做
在 domain 中建立一個 User 領域類和 Team 領域類
package com.rishiqing.test
class User {
String email
String nickName
static belongsTo = [
team : Team
]
}
package com.rishiqing.test
class Team {
String name
String logoUrl
String password
static hasMany = [
user : User
]
}
建立 UserDaoService ,並對 User 的 DAO 層進行編碼
package com.rishiqing.test.dao
import com.rishiqing.test.Team
import com.rishiqing.test.User
import com.rishiqing.test.dto.UserDTO
import grails.gorm.transactions.Transactional
@Transactional
class UserDaoService {
def getById (Long id) {
User.findById(id)
}
def getByEmail (String email) {
(User) User.createCriteria().get {
eq "email", email
}
}
def listByTeam (Team team) {
(List<User>) User.createCriteria().list {
eq "team", team
}
}
def save(User user) {
user.save()
}
def remove (User user) {
user.delete()
}
}
grails 框架自動生成的 UserDaoServiceSpec 單元測試文件
package com.rishiqing.test
import com.rishiqing.test.dao.UserDaoService
import grails.testing.services.ServiceUnitTest
import spock.lang.Specification
class UserDaoServiceSpec extends Specification implements ServiceUnitTest<UserDaoService>{
def setup() {
}
def cleanup() {
}
void "test something"() {
expect:"fix me"
true == false
}
}
經過命令,手動在相應位置建立集成測試文件(使用 IDEA Teminal 選項卡中的命令行)
D:\proj\testDemo>grails create-integration-test com.rishiqing.test.UserDaoServiceIntegration
| Created src/integration-test/groovy/com/rishiqing/test/UserDaoServiceIntegrationSpec.groovy
D:\proj\testDemo>
建立完成得 UserDaoServiceIntegrationSpec 集成測試文件
package com.rishiqing.demo
import grails.testing.mixin.integration.Integration
import grails.transaction.*
import spock.lang.Specification
@Integration (1)
@Rollback (2)
class DemoControllerIntegrationSpec extends Specification {
def setup() { (3)
}
def cleanup() {
}
void "test something"() {
expect:"fix me"
true == false
}
}
(1)使用 Integration 註解表示這是一個集成測試
(2)使用 Rollback 註解能夠確保每一個測試方法,在被回滾的事務中運行,而不會插入到數據庫中
(3) setup 方法
測試流程
編寫一個集成測試案例,測試 UserDaoService 中的 getById 方法
package com.rishiqing.test
import com.rishiqing.test.dao.UserDaoService
import grails.testing.mixin.integration.Integration
import grails.transaction.*
import org.springframework.beans.factory.annotation.Autowired
import spock.lang.Specification
@Integration
@Rollback
class UserDaoServiceIntegrationSpec extends Specification {
@Autowired (1)
UserDaoService userDaoService
void "test getById function"() {
when : (2)
def user = userDaoService.getById(1.toLong())
then: (3)
user == null
}
}
(1) spring beans 的自動注入,自動注入 userDaoService
(2) 先決條件
(3) 預測結果
點擊執行測試按鈕開始測試。
注意,選擇測試時,會有兩個選項,Grails 測試和 JUnit 測試。
由於 Grails 測試是基於 JUnit 測試的,所以會引入 JUnit 依賴,IDEA 在運行測試時,會檢測本項目支持的測試框架,因此會有兩個選項。
可是不能選擇 JUnit 測試,相對於 Grails ,JUnit 測試缺乏 GROM 環境,沒法執行 Grails 集成測試。會出現:
java.lang.IllegalStateException: No GORM implementations configured. Ensure GORM has been initialized correctly
異常,致使測試沒法執行。所以須要選擇 Grails 測試。
若是錯誤的選擇了 JUnit 測試,請在 IDEA 編輯項目配置位置刪除這個配置,從新選擇,並執行測試。
在類左側點執行按鈕
會執行此測試類中全部的測試方法
在方法左側點擊執行按鈕
,只會執行本測試方法
測試成功後,按鈕會變爲對勾狀態
測試失敗後,按鈕會出現異常狀
測試 getById 方法
class UserDaoServiceIntegrationSpec extends Specification {
...
void "test getById function"() {
when :
def user = userDaoService.getById(1.toLong())
then:
user == null
}
...
}
一個正常的測試結果
Testing started at 16:30 ...
"C:\Program Files\Java\jdk1.8.0_102\bin\java.exe" -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -XX:CICompilerCount=3 -Dgrails.full.stacktrace=true -Djline.WindowsTerminal.directConsole=false -Dfile.encoding=UTF-8 -classpath C:\Users\codingR\AppData\Local\Temp\classpath1263477126.jar org.grails.cli.GrailsCli intellij-command-proxy test-app com.rishiqing.test.UserDaoServiceIntegrationSpec -integration -echoOut --plain-output
|Resolving Dependencies. Please wait...
CONFIGURE SUCCESSFUL
Total time: 4.393 secs
:compileJava NO-SOURCE
:compileGroovy:buildProperties:processResources:classes
:compileTestJava NO-SOURCE
:compileTestGroovy:processTestResources NO-SOURCE
:testClasses
:compileIntegrationTestJava NO-SOURCE
:compileIntegrationTestGroovy:processIntegrationTestResources NO-SOURCE
:integrationTestClasses
:integrationTestListening for transport dt_socket at address: 13129
Connected to the target VM, address: '127.0.0.1:13129', transport: 'socket'
Grails application running at http://localhost:13189 in environment: test
Disconnected from the target VM, address: '127.0.0.1:13129', transport: 'socket'
:mergeTestReportsBUILD SUCCESSFUL
Total time: 21.018 secs
|Tests PASSED
Process finished with exit code 0
改動,測試一個異常的測試結果
class UserDaoServiceIntegrationSpec extends Specification {
...
void "test getById function"() {
when :
def user = userDaoService.getById(1.toLong())
then:
user != null
}
...
}
一個異常的測試結果
Testing started at 16:31 ...
"C:\Program Files\Java\jdk1.8.0_102\bin\java.exe" -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -XX:CICompilerCount=3 -Dgrails.full.stacktrace=true -Djline.WindowsTerminal.directConsole=false -Dfile.encoding=UTF-8 -classpath C:\Users\codingR\AppData\Local\Temp\classpath955494352.jar org.grails.cli.GrailsCli intellij-command-proxy test-app com.rishiqing.test.UserDaoServiceIntegrationSpec -integration -echoOut --plain-output
:compileJava NO-SOURCE
:compileGroovy UP-TO-DATE
:buildProperties UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileTestJava NO-SOURCE
:compileTestGroovy UP-TO-DATE
:processTestResources NO-SOURCE
:testClasses UP-TO-DATE
:compileIntegrationTestJava NO-SOURCE
:compileIntegrationTestGroovy:processIntegrationTestResources NO-SOURCE
:integrationTestClasses
:integrationTestListening for transport dt_socket at address: 13262
Connected to the target VM, address: '127.0.0.1:13262', transport: 'socket'
Grails application running at http://localhost:13306 in environment: test
Condition not satisfied:
user != null
| |
null false
Condition not satisfied:
user != null
| |
null false
at com.rishiqing.test.UserDaoServiceIntegrationSpec.$tt__$spock_feature_0_0(UserDaoServiceIntegrationSpec.groovy:20)
at com.rishiqing.test.UserDaoServiceIntegrationSpec.test something_closure1(UserDaoServiceIntegrationSpec.groovy)
at groovy.lang.Closure.call(Closure.java:414)
at groovy.lang.Closure.call(Closure.java:430)
at grails.gorm.transactions.GrailsTransactionTemplate$1.doInTransaction(GrailsTransactionTemplate.groovy:68)
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:133)
at grails.gorm.transactions.GrailsTransactionTemplate.executeAndRollback(GrailsTransactionTemplate.groovy:65)
at com.rishiqing.test.UserDaoServiceIntegrationSpec.test something(UserDaoServiceIntegrationSpec.groovy)
com.rishiqing.test.UserDaoServiceIntegrationSpec > test something FAILED
org.spockframework.runtime.ConditionNotSatisfiedError at UserDaoServiceIntegrationSpec.groovy:20
Disconnected from the target VM, address: '127.0.0.1:13262', transport: 'socket'
1 test completed, 1 failed
:integrationTest FAILED
:mergeTestReports
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':integrationTest'.
> There were failing tests. See the results at: file:///D:/proj/testDemo/build/test-results/integrationTest/
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
BUILD FAILED
Total time: 17.731 secs
Tests FAILED |
Test execution failed
Process finished with exit code 1
完善測試
完善 UserDaoServiceIntegrationSpec 對 UserDaoService 的全部方法的測試
package com.rishiqing.test.dao
import com.rishiqing.test.Team
import com.rishiqing.test.User
import com.rishiqing.test.dao.UserDaoService
import grails.testing.mixin.integration.Integration
import grails.transaction.*
import org.springframework.beans.factory.annotation.Autowired
import spock.lang.Specification
@Integration
@Rollback
class UserDaoServiceIntegrationSpec extends Specification {
@Autowired
UserDaoService userDaoService
void "test getById function"() { (1)
given: "初始化測試數據" (2)
def team = new Team()
team.name = '公司'
team.logoUrl = 'http://www.rishiqing.com'
team.save()
def user = new User()
user.nickName = '小明'
user.email = '1@rishiqing.com'
user.password = '123456'
user.team = team
user.save()
when:
def instance = userDaoService.getById(user.id)
then:
user.id == instance.id
}
void "test getByEmail function" () {
given:
def team = new Team()
team.name = '公司'
team.logoUrl = 'http://www.rishiqing.com'
team.save()
def user = new User()
user.nickName = '小明'
user.email = '1@rishiqing.com'
user.password = '123456'
user.team = team
user.save()
when:
def instance = userDaoService.getByEmail(user.email)
then:
instance.email == user.email
}
void "test listByTeam function" () {
given:
def team = new Team()
team.name = '公司'
team.logoUrl = 'http://www.rishiqing.com'
team.save()
def user = new User()
user.nickName = '小明'
user.email = '1@rishiqing.com'
user.password = '123456'
user.team = team
user.save()
when:
def users = userDaoService.listByTeam(team)
then:
users.size() == 1
}
void "test save function" () {
given:
def team = new Team()
team.name = '公司'
team.logoUrl = 'http://www.rishiqing.com'
team.save()
when:
def user = new User()
user.nickName = '小明'
user.email = '1@rishiqing.com'
user.password = '123456'
user.team = team
def instance = userDaoService.save(user)
then:
instance.email == '1@rishiqing.com'
}
void "test remove function" () {
given:
def team = new Team()
team.name = '公司'
team.logoUrl = 'http://www.rishiqing.com'
team.save()
def user = new User()
user.nickName = '小明'
user.email = '1@rishiqing.com'
user.password = '123456'
user.team = team
user.save()
when:
userDaoService.remove(user)
then:
User.findByEmail("1@qq.com") == null
}
}
(1) 能夠更改測試方法名,對須要測試的方法進行說明
(2) given 關鍵字用來描述測試數據,when 關鍵字用來描述測試方法,then 關鍵字用來描述測試結果
冒號後面能夠經過 "" 的方式添加說明
在編寫測試的環節中,須要注意被測試方法的惟一性。測試對應方法時,不要引入別的方法。
例如在測試 getById 方法時,須要先建立一個 User 對象,經過 User 對象的 id 測試 getById 方法。
在建立對象這一步,可使用 new User().save(),也可使用 userDaoService.save(new User()),顯然咱們不該該使用第二種方法,由於會對要測試的方法 getById 產生干擾。
更爲複雜的
按照 web 應用開發的規範,爲項目分層
DAO --> Manager --> Service --> Controller
| |
DTO VO
DAO 數據業務層,數據庫訪問,查詢
Manager 公共業務層,對接 DO 和 DTO,處理轉換及業務處理
Service 服務層,web 業務
Controller 控制器,接口
DTO 數據傳輸對象,業務層數據傳輸
VO 視圖對象,前端展現
使用上述分層,對用戶業務逐層編碼並進行測試。爲流程須要,先建立 UserDTO,UserVO ,用做用戶的數據傳輸和前端視圖
UserDTO
package com.rishiqing.test.dto
class UserDTO {
Long id
String email
String nickName
Long teamId
String teamName
String teamLogoUrl
}
UserVO
package com.rishiqing.test.vo
import com.rishiqing.test.dto.UserDTO
class UserVO {
static toMap (UserDTO userDTO) {
[
id : userDTO.id,
nickName: userDTO.nickName,
email: userDTO.email
]
}
}
Manager 層測試
編寫一個獲取用戶的方法,能夠經過 id 和 email 進行獲取,且返回給上層 DTO 對象
package com.rishiqing.test.manager
import com.rishiqing.test.dto.UserDTO
import com.rishiqing.test.exception.ParamNotFoundException
import grails.gorm.transactions.Transactional
@Transactional
class UserManagerService {
def userDaoService
def getUser(UserDTO userDTO) {
def instance
if (userDTO.id) {
instance = userDaoService.getById(userDTO.id)
} else if (userDTO.email) {
instance = userDaoService.getByEmail(userDTO.email)
} else {
throw new ParamNotFoundException()
}
userDTO.id = instance.id
userDTO.nickName = instance.nickName
userDTO.email = instance.email
return userDTO
}
}
Manager 層的集成測試,須要使用到 DAO 層模塊協同。測試文件 UserManagerServiceIntegrationSpec
package com.rishiqing.test.manager
import com.rishiqing.test.Team
import com.rishiqing.test.User
import com.rishiqing.test.dao.UserDaoService
import com.rishiqing.test.dto.UserDTO
import grails.gorm.transactions.Rollback
import grails.testing.mixin.integration.Integration
import org.springframework.beans.factory.annotation.Autowired
import spock.lang.Specification
@Integration
@Rollback
class UserManagerServiceIntegrationSpec extends Specification {
@Autowired
UserManagerService userManagerService
@Autowired
UserDaoService userDaoService
def setup () {
userManagerService.userDaoService = this.userDaoService (1)
}
void "test getUser function" () {
def team = new Team(
name: "公司",
logoUrl: "https://www.rishiqing.com",
).save()
def user = new User(
nickName: "小紅",
email: "2@rishiqing.com",
password: 123456,
team: team
).save()
when: "測試經過 id 獲取 DTO"
UserDTO userDTO = new UserDTO()
userDTO.id = user.id
userDTO = userManagerService.getUser(userDTO)
then:
userDTO.id == user.id
userDTO.nickName == user.nickName
userDTO.email == user.email
when: "測試經過 email 獲取 DTO" (2)
UserDTO userDTO1 = new UserDTO()
userDTO1.email = user.email
userDTO1 = userManagerService.getUser(userDTO1)
then:
userDTO1.id == user.id
userDTO1.nickName == user.nickName
userDTO1.email == user.email
}
}
(1) 須要給 userManagerService 中的 userDaoService 賦值
(2) 當有多個測試流程時,能夠分寫成多個 when ... then ... 添加更多的測試流程
對於 setup 方法,它適合作一些注入類初始化操做,並不適合在其中執行數據持久化操做,由於沒法回滾。
推薦的方式是在每一個測試中執行數據持久化操做,又或者提出公共方法,讓每一個測試方法調用公共方法,例如上述測試能夠改寫爲
package com.rishiqing.test.manager
import com.rishiqing.test.Team
import com.rishiqing.test.User
import com.rishiqing.test.dao.UserDaoService
import com.rishiqing.test.dto.UserDTO
import grails.gorm.transactions.Rollback
import grails.testing.mixin.integration.Integration
import org.springframework.beans.factory.annotation.Autowired
import spock.lang.Specification
@Integration
@Rollback
class UserManagerServiceIntegrationSpec extends Specification {
@Autowired
UserManagerService userManagerService
@Autowired
UserDaoService userDaoService
def setup () {
userManagerService.userDaoService = this.userDaoService
}
def initTestData () {
def team = new Team(
name: "公司",
logoUrl: "https://www.rishiqing.com",
).save()
def user = new User(
nickName: "小紅",
email: "2@rishiqing.com",
password: 123456,
team: team
).save()
return [team, user]
}
void "test getUser function" () {
given:
def (team, user) = initTestData()
when: "測試經過 id 獲取 DTO"
UserDTO userDTO = new UserDTO()
userDTO.id = user.id
userDTO = userManagerService.getUser(userDTO)
then:
userDTO.id == user.id
userDTO.nickName == user.nickName
userDTO.email == user.email
when: "測試經過 email 獲取 DTO"
UserDTO userDTO1 = new UserDTO()
userDTO1.email = user.email
userDTO1 = userManagerService.getUser(userDTO1)
then:
userDTO1.id == user.id
userDTO1.nickName == user.nickName
userDTO1.email == user.email
}
}
Service 層測試
userService 層編碼大同小異,須要多注入兩個 service
package com.rishiqing.test.service
import com.rishiqing.test.dto.UserDTO
import com.rishiqing.test.exception.DataNotFoundException
import com.rishiqing.test.exception.ParamNotFoundException
import grails.gorm.transactions.Transactional
import grails.web.servlet.mvc.GrailsParameterMap
@Transactional
class UserService {
def userManagerService
def getUser(GrailsParameterMap params) {
if (!params.get("email")) {
throw new ParamNotFoundException("email")
}
UserDTO userDTO = new UserDTO()
userDTO.email = params.get("email")
userDTO = userManagerService.getUser(userDTO)
if (!userDTO) {
throw new DataNotFoundException("user")
}
return userDTO
}
}
userServiceIntegrationSpec 文件編碼
package com.rishiqing.test.service
import com.rishiqing.test.Team
import com.rishiqing.test.User
import com.rishiqing.test.dao.UserDaoService
import com.rishiqing.test.manager.UserManagerService
import grails.testing.mixin.integration.Integration
import grails.transaction.*
import grails.web.servlet.mvc.GrailsParameterMap
import org.springframework.beans.factory.annotation.Autowired
import spock.lang.Specification
@Integration
@Rollback
class UserServiceIntegrationSpec extends Specification {
@Autowired
UserService userService
@Autowired
UserManagerService userManagerService
@Autowired
UserDaoService userDaoService
def setup () {
userService.userManagerService = this.userManagerService
userService.userManagerService.userDaoService = this.userDaoService
}
def initTestData () {
def team = new Team(
name : '公司',
logoUrl: "https://www.rishiqing.com"
).save()
def user = new User(
nickName: "小李",
email: '3@rishiqing.com',
password: 123456,
team: team
).save()
return [team, user]
}
void "test getUser function"() {
given: "初始化測試數據"
initTestData()
when:
def params = new GrailsParameterMap([
email: "3@rishiqing.com"
],null)
def userDTO = userService.getUser(params)
then:
params.email == userDTO.email
}
}
Controller 層測試
controller 層測試,須要在引入集成測試的同時,實現單元測試框架。
實現單元測試框架後,就可使用 params ,request ,response 等公共變量,捨去不少 grails 框架爲 controller 作的初始化操做,能夠直接進入測試流程。
@Integration
@Rollback
class UserControllerIntegrationSpec extends Specification implements ControllerUnitTest<UserController> {
...
}
UserController 控制器
package com.rishiqing.test
import com.rishiqing.test.exception.ServerException
import com.rishiqing.test.vo.UserVO
import grails.converters.JSON
class UserController {
def userService
def show() {
try {
def userDTO = userService.getUser(params)
render UserVO.toMap(userDTO) as JSON
} catch (Exception e) {
log.error("系統錯誤" + e.message)
def se = new ServerException()
render se.renderMap as JSON
}
}
}
UserControllerIntegrationSpec 測試文件
package com.rishiqing.test
import com.rishiqing.test.dao.UserDaoService
import com.rishiqing.test.manager.UserManagerService
import com.rishiqing.test.service.UserService
import grails.gorm.transactions.Rollback
import grails.testing.mixin.integration.Integration
import grails.testing.web.controllers.ControllerUnitTest
import org.grails.web.json.JSONObject
import org.springframework.beans.factory.annotation.Autowired
import spock.lang.Specification
@Integration
@Rollback
class UserControllerIntegrationSpec extends Specification
implements ControllerUnitTest<UserController> {
@Autowired
UserService userService
@Autowired
UserManagerService userManagerService
@Autowired
UserDaoService userDaoService
def setup () {
controller.userService = this.userService (1)
userService.userManagerService = this.userManagerService
userService.userManagerService.userDaoService = this.userDaoService
}
def initTestData () {
def team = new Team(
name : '公司',
logoUrl: "https://www.rishiqing.com"
).save()
def user = new User(
nickName: "小趙",
email: '4@rishiqing.com',
password: 123456,
team: team
).save()
return [team, user]
}
void "test userController getUser interface" () {
given:
initTestData()
when:
params.email = "4@rishiqing.com" (2)
controller.show() (3)
then:
JSONObject o = new JSONObject(
response.json.toString()) (4)
o.email == params.get("email")
}
}
(1) 能夠直接使用公共變量 controller,此 controller 指代 implements ControllerUnitTest<UserController> 中的 UserController
(2) 請求參數Map GrailsParameterMap 也能夠做爲公共變量使用
(3) 直接調用 show() 接口,會自動引入 params 變量,來模擬請求
(4) 使用公共變量 response,能夠獲取 show() 接口 render 的數據,來驗證數據
最後的流程,Grails 功能測試
功能測試涉及針對正在運行的應用程序發出HTTP請求並驗證結果行爲。這對於端到端測試場景很是有用,例如針對JSON API進行REST調用。
使用Grails 功能測試用來驗證功能的完整性。
功能而是須要在 build.gradle 導入依賴
dependencies {
testCompile "org.grails:grails-datastore-rest-client"
}
新建 UserController 的功能測試文件
package com.rishiqing.test
import grails.gorm.transactions.Rollback
import grails.plugins.rest.client.RestBuilder
import grails.plugins.rest.client.RestResponse
import grails.testing.mixin.integration.Integration
import spock.lang.Shared
import spock.lang.Specification
@Integration
@Rollback
class UserControllerFunctionalSpec extends Specification{
@Shared (1)
RestBuilder rest = new RestBuilder()
def setup () {
new User( (2)
nickName: "小張",
email: '5@rishiqing.com',
password: 123456,
team: new Team(
name : '公司',
logoUrl: "https://www.rishiqing.com"
).save()
).save()
}
void "test user show interface functional" () {
given:
String email = "5@rishiqing.com"
when:
RestResponse resp = rest.get("http://127.0.0.1:${serverPort}/user/show?email=${email}") (3)
then:
resp.status == 200
resp.json.size() == 3
resp.json.email == email
}
}
(1) Shared 註解表示此對象共享,每一個測試方法不用從新建立 RestBuilder 對象
(2) 在 setup 中建立測試數據。(3) 中發送請求和當前測試屬於兩個不一樣的事務,若是使用 initTestData 的初始化方式,(3) 中的請求沒法訪問到數據
(3) serverPort 屬性是自動注入的。它包含Grails應用程序在功能測試期間運行的任意文件
文件目錄結構
參考文檔
轉載請註明出處 2019年7月12日16點53分 —— codingR