【问题标题】:How to create a generic CRUD controller test in Spock如何在 Spock 中创建通用 CRUD 控制器测试
【发布时间】:2021-06-16 00:36:58
【问题描述】:

是否可以在 Spock 中为 Spring 控制器创建通用单元测试?我在 Spring Boot 中有一个抽象控制器,它由一些特定的控制器扩展。结果是每个控制器都有相同的 CRUD 实现。所以,现在我想为这些控制器创建类似的单元测试,但我不能在 Spock 测试中使用构造函数。我得到错误

CrudControllerTest.groovy
Error:(16, 5) Groovyc: Constructors are not allowed; instead, define a 'setup()' or 'setupSpec()' method
IngredientControllerTest.groovy
Error:(7, 5) Groovyc: Constructors are not allowed; instead, define a 'setup()' or 'setupSpec()' method

以下代码

abstract class CrudControllerTest<T, R extends JpaRepository<T, Long>, C extends CrudController<T,R>> extends Specification {
    private String endpoint
    private def repository
    private def controller
    private MockMvc mockMvc
 
    CrudControllerTest(def endpoint, R repository, C controller) {
        this.endpoint = endpoint
        this.repository = repository
        this.controller = controller
        this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build()
    }
 
    def "Should get 404 when product does not exists"() {
        given:
        repository.findById(1) >> Optional.empty()
        when:
        def response = mockMvc.perform(MockMvcRequestBuilders.get(endpoint + '/1')).andReturn().response
        then:
        response.status == HttpStatus.NOT_FOUND.value()
    }
}
 
class IngredientControllerTest extends CrudControllerTest<Ingredient, IngredientRepository, IngredientController> {
    
    IngredientControllerTest() {
        def repository = Mock(IngredientRepository)
        super("/ingredients", repository, new IngredientController(Mock(repository)))
    }
}

这里还有其他方法可以在 Spock 中实现通用单元测试吗?

【问题讨论】:

    标签: java unit-testing spock


    【解决方案1】:

    Specifications 不能使用构造函数,而是可以使用模板方法模式。使用单独的方法:

    abstract class CrudControllerTest extends Specification {
        private String endpoint
        private def repository
        private def controller
        private MockMvc mockMvc
     
        def setup() {
            endpoint = createEndpoint()
            repository = createRepository()
            controller = createController()
            mockMvc = MockMvcBuilders.standaloneSetup(controller).build()
        }
        
        abstract createEndpoint()
        abstract createRepository()
        abstract createController()
     
        def "Should get 404 when product does not exists"() {
            given:
            repository.findById(1) >> Optional.empty()
            when:
            def response = mockMvc.perform(MockMvcRequestBuilders.get(endpoint + '/1')).andReturn().response
            then:
            response.status == HttpStatus.NOT_FOUND.value()
        }
    }
     
    class IngredientControllerTest extends CrudControllerTest<Ingredient, IngredientRepository, IngredientController> {
        
        def createEndpoint() {
            "/ingredients"
        }
        def createRepository {
            Mock(IngredientRepository)
        }
        def createController() {
            new IngredientController(repository)
        }
    }
    

    或者使用返回所有内容的方法,这会更好一些,因为您的某些值需要引用另一个值:

    abstract class CrudControllerTest extends Specification {
        private String endpoint
        private def repository
        private def controller
        private MockMvc mockMvc
     
        def setup() {
            (endpoint, repository, controller) = createSut()
            mockMvc = MockMvcBuilders.standaloneSetup(controller).build()
        }
        
        abstract createSut()
     
        def "Should get 404 when product does not exists"() {
            given:
            repository.findById(1) >> Optional.empty()
            when:
            def response = mockMvc.perform(MockMvcRequestBuilders.get(endpoint + '/1')).andReturn().response
            then:
            response.status == HttpStatus.NOT_FOUND.value()
        }
    }
     
    class IngredientControllerTest extends CrudControllerTest<Ingredient, IngredientRepository, IngredientController> {
        
        def createSut() {
            def repo = Mock(IngredientRepository)
            ["/ingredients", repo, new IngredientController(repository)]
        }
    }
    

    【讨论】:

    • 它有效,谢谢。我在设置之前只添加了“def”
    • 在示例中添加了缺少的def
    猜你喜欢
    • 1970-01-01
    • 2016-12-30
    • 1970-01-01
    • 2015-01-07
    • 1970-01-01
    • 1970-01-01
    • 2017-03-07
    • 2016-12-12
    • 1970-01-01
    相关资源
    最近更新 更多