欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring Boot:对DAO、Service和Controller进行单元测试

程序员文章站 2022-04-26 10:31:54
...

Spring mvc尽可能把逻辑封装在Model层,Controller只负责UI展示的处理,这样Model层就更加容易复用,在各个模块进行调用。同时也方便单元测试,文章代码Kotlin语言编写。

DAO & Service层单元测试

dao和Service的测试比较简单,就是通过Spring mvc框架的自动注入获取到实例,即可对接口实现测试。

@Repository
interface UserRepository {

    @Select("SELECT * FROM User WHERE ID = #{id}")
    fun findById(@Param("id") integer: Int?): User

    @Select("SELECT * FROM User")
    fun findAll(): List<User>

    @Insert("INSERT INTO User(id,name) VALUES(#{id}, #{name})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    fun insert(user: User)
}

测试UserRepository,只需要使用@RunWith@SpringBootTest就能确定单元测试的环境,实现自动注入。

@RunWith(SpringRunner::class)
@SpringBootTest
class UserRepositoryTest {

    @Autowired
    lateinit var repository: UserRepository

    @Test
    @Throws(Exception::class)
    fun findById() {
        val user = repository.findById(1)
        assertNotNull(user)
        assertEquals(1, user.id)
        println("id:" + user.id + ",name:" + user.name)
    }
    @Test
    @Throws(Exception::class)
    fun findAll() {
        val users = repository.findAll()
        users.forEach { println(it.id) }
    }

    @Test
    @Throws(Exception::class)
    fun insert() {
        val user = User(1, "Wiki")
        repository.insert(user)
    }
}

Controller单元测试

Controller层是我们面向用户的层,负责把Model层的数据按照一定的格式返回给用户,通常而言都是json网页

方案一:Java接口测试,和dao层测试类同(推荐)

我们知道可以通过request.setAttribute对页面进行传参数

// 不推荐
@Controller
class UserController {
    @RequestMapping("/user")
    fun show(request: HttpServletRequest, id:Int): String {
        val user = User(id, "Wiki")
        request.setAttribute("name", "Wiki")
        return "/user.jsp"
    }
}

但是这种方式并不方便测试,建议使用以下方式对页面进行传参数,建议采用一下方案进行编写代码,一方面逻辑清晰,另一方面方便进行单元测试。

// 推荐
@Controller
class UserController {
    @RequestMapping("/user")
    fun show(map: MutableMap<String, Any>, id: Int): String {
        val user = User(id, "Wiki")
        map["user"] = user
        return "/user.jsp"
    }
}

那么对UserController进行单元测试就变得非常的简单,不涉及到JSON、网页

@RunWith(SpringRunner::class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class UserControllerTest {

    @Autowired
    lateinit var controller: UserController

    @Test
    @Throws(Exception::class)
    fun show() {
        val map = HashMap<String, Any>()
        controller.show(map, 1)
        val user = map["user"] as User
        assertNotNull(user)
        assertEquals(1, user.id)
        println("id:" + user.id + ",name:" + user.name)
    }
}

下面举例测试返回json的@RestController

@RestController
class UserApiController {

    @RequestMapping("/api/getUser")
    fun getUser(id: Int): User {
        val user = User(id, "Wiki")
        return user
    }
}

测试代码:

@RunWith(SpringRunner::class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class UserApiControllerTest {
    @Autowired
    lateinit var controller: UserApiController

    @Test
    @Throws(Exception::class)
    fun getUser() {
        val user = controller.getUser(1)
        assertNotNull(user)
        assertEquals(1, user.id)
        println("id:" + user.id + ",name:" + user.name)
    }
}
方案二:MockMvc

上述的方案一其实和Service测试都是类似的,但是有时候我们还是需要测试真实反馈的内容,包括网页,JSON信息,使用MockMvc也可以实现。

@RunWith(SpringRunner::class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
class UserApiControllerMockMvcTest {
    @Autowired
    lateinit var mvc: MockMvc
    @Test
    @Throws(Exception::class)
    fun getUser() {
        mvc.perform(MockMvcRequestBuilders.get("/api/getUser?id=1").accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk)
                .andExpect(content().string(equalTo<String>("{\"id\":1,\"name\":\"Wiki\"}")))
    }
}
方案三:TestRestTemplate

,但是有时候我们还是需要使用URL对方案进行测试,通过访问URL,对比返回的内容,属于真实的浏览器请求。

@RunWith(SpringRunner::class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class UserApiControllerTemplateTest {
    @LocalServerPort
    var port: Int = 0
    lateinit var base: URL
    @Autowired
    lateinit var template: TestRestTemplate
    @Before
    @Throws(Exception::class)
    fun setUp() {
        this.base = URL("http://localhost:$port/")
    }
    @Test
    @Throws(Exception::class)
    fun getUser() {
        val response = template.getForEntity(base.toString() + "api/getUser?id=1",String::class.java)
        assertThat(response.body, equalTo<String>("{\"id\":1,\"name\":\"Wiki\"}"))
    }
}

Demo代码

https://github.com/taoweiji/GradleKotlinSpringBootMybatisSQLiteDemo