那个(结束了吗?)延长使用服务基类单元测试的Grails域(Unit testing Grails

2019-10-21 15:13发布

所以,我有,我想我的领域类的大多数(但不是全部)延长从基类。 我们的目标是,我可以加我需要任何域类用一个简单的六个审计信息的列extends 。 对于这两种创建和更新,我想登录的日期,用户和程序(基于请求URI)。 它使用一个Grails服务(称为CasService)来查找当前登录的用户。 该CasService然后使用Spring安全性和数据库调用来获取该领域的相关用户信息。

麻烦的是,如果我这样做,那么我将不得不莫克在测试使用这些类域的任何单元测试的CasService和请求对象。 这也将会影响服务,并使用这些域控制器单元测试。 这将会使单元测试有点痛,增加锅炉板代码,这是我试图避免。

我在钓鱼更好的设计方案,而我接受建议。 我现在的默认是简单地将相同的锅炉板添加到我的所有域类和用它做。 请参见下面的源代码,什么到目前为止,我已经试过。

资源

常见的审计域类

package com.mine.common

import grails.util.Holders
import org.springframework.web.context.request.RequestContextHolder

class AuditDomain implements GroovyInterceptable {

    def casService = Holders.grailsApplication.mainContext.getBean('casService')
    def request = RequestContextHolder?.getRequestAttributes()?.getRequest()

    String creator
    String creatorProgram
    String lastUpdater
    String lastUpdateProgram

    Date dateCreated
    Date lastUpdated

    def beforeValidate() {
        beforeInsert()
        beforeUpdate()
    }

    def beforeInsert() {
        if (this.creator == null) {
            this.creator = casService?.getUser() ?: 'unknown'
        }

        if (this.creatorProgram == null) {
            this.creatorProgram = request?.requestURI ?: 'unknown'
        }
    }

    def beforeUpdate() {
        this.lastUpdater = casService?.getUser() ?: 'unknown'
        this.lastUpdateProgram = request?.requestURI ?: 'unknown'
    }

    static constraints = {
        creator nullable:true, blank: true
        lastUpdater nullable:true, blank: true
        creatorProgram nullable:true, blank: true
        lastUpdateProgram nullable:true, blank: true
     }
}

CasService

package com.mine.common

import groovy.sql.Sql

class CasService {
    def springSecurityService, sqlService, personService

    def getUser() {
        if (isLoggedIn()) {
            def loginId = springSecurityService.authentication.name.toLowerCase()
            def query = "select USER_UNIQUE_ID from some_table where USER_LOGIN = ?"
            def parameters = [loginId]
            return sqlService.call(query, parameters)
        } else {
            return null
        }
    }

    def private isLoggedIn() {
        if (springSecurityService.isLoggedIn()) {
            return true
        } else {
            log.info "User is not logged in"
            return false
        }
    }

    //...
}

我已经试过

创建测试工具类做安装逻辑

我试图建立这样一个类:

class AuditTestUtils {

    def setup() {
        println "Tell AuditDomain to sit down and shut up"
        AuditDomain.metaClass.casService = null
        AuditDomain.metaClass.request = null
        AuditDomain.metaClass.beforeInsert = {}
        AuditDomain.metaClass.beforeUpdate = {}
    }

    def manipulateClass(classToTest) {
        classToTest.metaClass.beforeInsert = {println "Yo mama"}
        classToTest.metaClass.beforeUpdate = {println "Yo mamak"}
    }
}

然后在我的单元测试的设置()和setupSpec称之为()块:

def setupSpec() {
    def au = new AuditTestUtils()
    au.setup()
}

要么

def setupSpec() {
    def au = new AuditTestUtils()
    au.manipulateClass(TheDomainIAmTesting)
}

没有骰子。 也就是说,只要我尝试保存扩展AuditDomain域类的CasService一个NullPointerException错误的。

java.lang.NullPointerException: Cannot invoke method isLoggedIn() on null object
    at com.mine.common.CasService.isLoggedIn(CasService.groovy:127)
    at com.mine.common.CasService.getPidm(CasService.groovy:9)
    at com.mine.common.AuditDomain.beforeInsert(AuditDomain.groovy:26)
    //...
    at org.grails.datastore.gorm.GormInstanceApi.save(GormInstanceApi.groovy:161)
    at com.mine.common.SomeDomainSpec.test creation(SomeDomainSpec:30)

我开到接近干燥的审计信息我的域名问题的替代方法。 我已经看中了继承 ,但也有其他办法。 性状都没有在我的环境(的Grails 2.3.6)可用,但也许这只是一个原因,去上更新到最新版本开裂。

我也开到如何以不同的方式测试这些领域的建议。 也许我应该在有他们每个域类的单元测试的审计列抗衡,但我宁愿不。 我可以接受处理,在集成测试,但我可以单元测试AuditDomain类很轻松地对自己。 我宁愿是我的域的单元测试测试的具体事物这些领域带来的表,而不是普通的东西,他们都有。

Answer 1:

因此,最终,我已经使用Groovy代表团来满足这种需求。

验证逻辑的所有生命

class AuditDomainValidator extends AuditDomainProperties {

    def domainClassInstance 

    public AuditDomainValidator(dci) {
        domainClassInstance = dci
    }

    def beforeValidate() {
        def user = defaultUser()
        def program = defaultProgram()
        if (this.creator == null) {
            this.creator = user
        }

        if (this.creatorProgram == null) {
            this.creatorProgram = program
        }
        this.lastUpdater = user
        this.lastUpdateProgram = program
    }

    private def defaultProgram() {
        domainClassInstance.getClass().getCanonicalName()
    }

    private def defaultUser() {
        domainClassInstance.casService?.getUser() ?: 'unknown'
    }
}

我创造了这个抽象类,而试图各种解决方案,以保持性能。 这也许可以折叠成没有问题的验证器类,但我只是够懒把它留在里面,因为它的工作。

abstract class AuditDomainProperties {
    String creator
    String creatorProgram
    String lastUpdater
    String lastUpdateProgram

    Date dateCreated
    Date lastUpdated
}

最后,这里是如何实现一个Grails领域类的验证器类。

import my.company.CasService
import my.company.AuditDomainValidator

class MyClass {
    def casService

    @Delegate AuditDomainValidator adv = new AuditDomainValidator(this)
    static transients = ['adv']
    //...domain class code

    static mapping = {
        //..domain column mapping
        creator column: 'CREATOR'
        lastUpdater column: 'LAST_UPADTER'
        creatorProgram column: 'CREATOR_PGM'
        lastUpdateProgram column: 'LAST_UPDATE_PGM'
        dateCreated column: 'DATE_CREATED'
        lastUpdated column: 'LAST_UPDATED'
    }
}

这种方法不适用于单元和集成测试很好地工作,它似乎。 试图在任何失败,并没有这样的属性在问题域类错误访问dateCreated会列。 那奇怪,因为运行的应用程序工作正常。 与单元测试,我会认为这是一个嘲讽的问题,但我不会期望这是在集成测试的一个问题。



文章来源: Unit testing Grails domains that (over?) extend a base class that uses services