简体   繁体   English

如何在Grails应用程序中使用Spock来测试具有真实交易的服务?

[英]How to use spock to test a service with real transactions in Grails application?

I am using Grails 2.4.4 and want to apply spock tests in my project. 我正在使用Grails 2.4.4,并想在我的项目中应用spock测试。

Below is the MemberService : 以下是MemberService

@Transactional
class MemberService {

    def lastMember = null

    def login (userId, password) {

        def member = Member.findByLoginEmail(userId)
        if(!member) return LoginResult.NO_SUCH_MEMBER
        if (member.isLocked) return LoginResult.MEMBER_IS_LOCKED

        log.debug("member.password is [${member.passwd}], input password is [${password}]")

        if (!member.passwd.equals(password)) return LoginResult.PASSWORD_ERROR

        switch(member.validateResult) {
            case "FAILED":
                return LoginResult.VAILDATE_FAILED
            case "WAIT":
                return LoginResult.WAIT_VALIDATE
        }

        member.lasLoginTime = new Timestamp(System.currentTimeMillis())
        member.lastLoginPlatform = "WEB"
        member.loginFailCount = 0
        member.save()
        lastMember = member
        return LoginResult.SUCCESS
    }

    enum LoginResult {
        SUCCESS,
        NO_SUCH_MEMBER,
        PASSWORD_ERROR,
        MEMBER_IS_LOCKED,
        VAILDATE_FAILED,
        WAIT_VALIDATE
    }

    enum ValidateResult {
        SUCCESS,
        FAILED,
        WAIT
    }
}

MemberServiceSpec is as below: MemberServiceSpec如下:

@TestFor(MemberService)
@Mock(Member)
class MemberServiceSpec extends Specification {
    def memberService

    def setup() {
        memberService = new MemberService()
    }

    void "test something"() {

        when:
            println "service is ${service}"
            def result = memberService.login("aa@mymail.com", "123456")
            println "result is ${result}"
        then:
            result == MemberService.LoginResult.SUCCESS
    }
}

The test result is as below: 测试结果如下:

Testing started at 15:15 ...
|Loading Grails 2.4.4
|Configuring classpath
.
|Environment set to test
....................................
|Running without daemon...
..........................................
|Compiling 1 source files
.
|Running 2 unit tests...|Running 2 unit tests... 1 of 2
--Output from test something--
1. setup
service is cusine_market.MemberService@54c425b1
Enter MemberService.login----------------userId=[aa@mymail.com]
result is NO_SUCH_MEMBER
Failure:  |
test something(cusine_market.MemberServiceSpec)
 |
Condition not satisfied:
result == MemberService.LoginResult.SUCCESS
|      |
|      false
NO_SUCH_MEMBER
    at cusine_market.MemberServiceSpec.test something(MemberServiceSpec.groovy:32)
Condition not satisfied:

result == MemberService.LoginResult.SUCCESS
|      |
|      false
NO_SUCH_MEMBER

Condition not satisfied:

result == MemberService.LoginResult.SUCCESS
|      |
|      false
NO_SUCH_MEMBER

    at cusine_market.MemberServiceSpec.test something(MemberServiceSpec.groovy:32)

|Completed 1 unit test, 1 failed in 0m 6s
.Tests FAILED 

I confirm that the user exists in database. 我确认该用户存在于数据库中。

Could anyone tell me why MemberService can not find the user "aa@mymail.com" in database? 谁能告诉我为什么MemberService在数据库中找不到用户“ aa@mymail.com”? I also tried the below line 我也尝试了以下行

    memberService = Mock(MemberService);

The result is the same. 结果是一样的。 However, if I run-app, the service does find the user. 但是,如果我运行应用程序,则该服务会找到用户。

Mocking 惩戒

In your test you're testing MemberService , which means you don't need to mock it . 在测试中,您正在测试MemberService ,这意味着您无需对其进行模拟 Just refer to it via service . 只需通过service引用即可。

Test Data 测试数据

This looks like unit test, not the integration one. 这看起来像单元测试,而不是集成测试。 So it doesn't use your database at all. 因此,它根本不使用您的数据库。

What you need to do is to create the Member instance manually in test. 您需要做的是在测试中手动创建Member实例。 You have it in your @Mock annotation, which is good. 您可以在@Mock批注中添加它,这很好。

Now, create the object, preferably in a given block: 现在,最好在given块中创建对象:

void "test something"() {
    given:
        new Member(loginEmail: 'aa@mymail.com', password: '123456', ...).save(failOnError: true, flush: true)

    when:
        println "service is ${service}"
        def result = service.login("aa@mymail.com", "123456")
        println "result is ${result}"
    then:
        result == MemberService.LoginResult.SUCCESS
}

I added failOnError: true to make sure the object has actually been created. 我添加了failOnError: true以确保实际上已创建对象。 You - of course - need to provide all required properties in Member constructor and make sure the data you provide correspond to the one you provide to login method. 您-当然-需要在Member构造函数中提供所有必需的属性,并确保您提供的数据与您提供给login方法的数据相对应。

Also, you need to ensure the Member object is initialized in a way that fulfills the path you want to reach. 另外,您需要确保以满足您要到达的路径的方式初始化Member对象。 For example, if you want to reach LoginResult.SUCCES state, you need to set member.isLocked to false , etc. 例如,如果要达到LoginResult.SUCCES状态,则需要将member.isLocked设置为false ,等等。

BONUS 奖金

When you get this test to work, you may want to take a look into Build Test Data Plugin . 当您使该测试生效时,您可能想看看Build Test Data Plugin It makes creating test data - like your Member object - much easier. 它使创建测试数据(如您的Member对象)更加容易。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM