spock框架中的继承。我想在多个规范中重用测试用例

时间:2016-03-03 20:37:22

标签: grails spock

在我的项目中,我有许多彼此非常相似的域对象。在设计测试时,我意识到很多测试用例都是相同的。所以我设置自己创建一个BaseSpec,使我能够重用逻辑和代码。

  • 我在下面描述的方法是否正确。
  • 除了BaseSpec,我将如何执行所有单元测试?

假设我有以下两个grails域类:

class Product{
    String productName
    static constraints = {
        name nullable: false, blank: false, unique: true
    }

}

class Category{
    String categoryName
    static constraints = {
        name nullable: false, blank: false, unique: true
    }
}

首先,我需要一个Spec作为基础。此基础将具有ProductSpec和Category Spec的常见测试。

@Mock([Product, Category])  //? is this needed
class BaseSpec extends Specification {
    @Shared classForTest
    @Shared constructor
    @Shared nameParam

    def setup(){
        //This is what I would override in my child Tests
        //classForTest =  new Category().getClass()
        //constructor = classForTest.getConstructor()
        //nameParam = "categoryName"

    }


    def "Testing that the name field is unique"(){
        given: "A class instance"
            String testValue = "ABSCSD SDS SDS"
            def instance1 = constructor.newInstance()  //using the constructor to get an instance
            instance1."${nameParam}" = testValue
            instance1.save(flush: true, failOnError: true)
        and: "A duplicated Instance"
            def instance2 = constructor.newInstance()
            instance2."${nameParam}" = testValue
        expect: "The second instance should not save"
            shouldFail {instance2.save(flush: true, failOnError: true)}
    }

    //...Other common test cases


}

然后我会有一个ProductSpec和一个CategorySpec,每个应该只覆盖BaseSpec的设置。

@TestFor(Product)
class ProductSpec extends BaseSpec{
    @Override
    def setup(){
        classForTest =  new Product().getClass()  //Get the class that is testec
        constructor = classForTest.getConstructor() //Get the constructor of the tested class
        nameParam = "productName" //Establish the name of the parameter
    }

    //... Other Product Tests
}

@TestFor(Category)
class CategorySpec extends BaseSpec {
    @Override
    def setup(){
        classForTest =  new Category().getClass()
        constructor = classForTest.getConstructor()
        nameParam = "categoryName"
    }
    //... Other Category Tests
}

1 个答案:

答案 0 :(得分:0)

这是我最初发布的内容的替代方案。 它不使用继承,但它允许测试用例非常相似的测试用例。

Consider the domain classes
class Product{
    String productName
    static constraints = {
        name nullable: false, blank: false, unique: true
    }

}

class Category{
    String categoryName
    static constraints = {
        name nullable: false, blank: false, unique: true
    }
}

作为一个非常简单的例子,我们想测试productName和categoryName的验证。所以我们可以进行如下测试:

@Mock([Product, Category])
class CatalogSimpleGenericSpec extends Specification {

    def "Validation of test field in a given domain"(){
        given: "A new instance of the domain"
            def testInstance = constructorOfInstance.newInstance()  //Creating the instance using the constructor
            testInstance."${fieldName}" = fieldValue 
            testInstance.validate()

        expect: "Testing if the instance should save depending on the value of shouldHaveErrors"
            assert testInstance.hasErrors() == shouldHaveErrors //Verifying if it should have errors

            //If there are errors the instance should fail to save, otherwise it will save perfectly
            shouldHaveErrors ?
                shouldFail {testInstance.save(flush: true, failOnError: true)}
                : testInstance.save(flush: true, failOnError: true)

        where:
            constructorOfInstance                               | shouldHaveErrors  | fieldName | fieldValue                | reasoning
            //-----------------  PRODUCT ----------------------
            new Product().getClass().getConstructor()           | false             | "productName" | "Folders"             | "Should pass since a product name is supplied"
            new Product().getClass().getConstructor()           | true              | "productName" | ""                    | "Should fail empty strings should not be accepted"
            new Product().getClass().getConstructor()           | true              | "productName" | null                  | "Should fail null values shoult not be accepted"

            //----------------- CATEGORY --------------------
            new Category().getClass().getConstructor()          | false             | "categoryName" | "Office Supplies"    | "Should pass since a category name is supplied"
            new Category().getClass().getConstructor()          | true              | "categoryName" | ""                   | "Should fail empty strings should not be accepted"
            new Category().getClass().getConstructor()          | true              | "categoryName" | null                 | "Should fail null values shoult not be accepted"
    }


} 

我没有使用继承,而是获取域实例的构造函数,然后使用该构造函数(constructorOfInstance)来构建我的实例(testInstance)。字段值使用testInstance."${fieldName}" = fieldValue指定。我们使用 shouldHaveErrors 的值测试保存是否会失败。