带有 Kotlin 控制器的 Mockito class
Mockito with Kotlin Controller class
我正在尝试使用 mockito 版本 3.7.0 来测试我的 Java 11 Spring Boot Kotlin 控制器。我正在使用 Spring Boot 的 2.4.1 版
我的 pom 导入如下
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.7.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>3.7.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>3.7.0</version>
<scope>test</scope>
</dependency>
当我运行我的测试时,我得到如下错误
java.lang.NullPointerException: Parameter specified as non-null is null: method net.app.service.smelter.SmelterMapper.toDtoPage, parameter entityPage
at net.app.service.smelter.SmelterMapper.toDtoPage(SmelterDto.kt)
at net.app.web.SmelterControllerTest.testListAllSmelters_Success1ItemReturned(SmelterControllerTest.kt:124)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.mockito.internal.runners.DefaultInternalRunner.evaluate(DefaultInternalRunner.java:54)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access0(ParentRunner.java:66)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:99)
at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:105)
at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:40)
at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:163)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53)
,
我想知道测试我的控制器的最佳方法是什么class - 这是我现在所拥有的
SmelterControllerTest
package net.app.web
import net.web.domain.Smelter
import net.web.service.smelter.SmelterDto
import net.web.service.smelter.SmelterMapper
import net.web.service.smelter.SmelterService
import org.junit.jupiter.api.Test
import org.mockito.Mockito
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.boot.test.mock.mockito.MockBean
import org.springframework.data.domain.Page
import org.springframework.data.domain.PageImpl
import org.springframework.data.domain.Pageable
import org.springframework.http.MediaType
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultHandlers
import org.springframework.test.web.servlet.result.MockMvcResultMatchers
import java.util.*
@WebMvcTest(controllers = [SmelterController::class], )
internal class SmelterControllerTest @Autowired constructor(
private val mockMvc: MockMvc,
) {
companion object {
private val BASE_ENDPOINT = "/api/smelter"
}
@MockBean
private lateinit var smelterService: SmelterService
@MockBean
private lateinit var smelterMapper: SmelterMapper
@Test
fun `list all smelters returns one item`() {
val smelterList = LinkedList<Smelter>()
smelterList.add(Smelter())
val smelters = PageImpl(smelterList)
val smelterDtoList = LinkedList<SmelterDto>()
val smelterDto = SmelterDto()
smelterDto.id = "id"
smelterDto.name = "name"
smelterDto.metal = "value"
smelterDtoList.add(smelterDto)
val smelterDtos = PageImpl(smelterDtoList)
Mockito.`when`(smelterService.listAll(Mockito.isA(Pageable::class.java))).thenReturn(smelters)
Mockito.`when`(smelterMapper.toDtoPage(Mockito.isA(Page::class.java) as Page<Smelter>, Mockito.isA(Pageable::class.java))).thenReturn(smelterDtos)
mockMvc.perform(
MockMvcRequestBuilders.get("$BASE_ENDPOINT/list?page=1&size=19&sort=id")
.contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().is2xxSuccessful)
.andExpect(MockMvcResultMatchers.jsonPath("$").exists())
.andExpect(MockMvcResultMatchers.jsonPath("$.content").exists())
.andExpect(MockMvcResultMatchers.jsonPath("$.content[0].id").value(smelterDtoList.get(0).id.toString()))
.andExpect(MockMvcResultMatchers.jsonPath("$.content[0].name").value(smelterDtoList.get(0).name.toString()))
.andExpect(MockMvcResultMatchers.jsonPath("$.content[0].metal").value(smelterDtoList.get(0).metal.toString())).andReturn()
Mockito.verify(smelterService , Mockito.times(1)).listAll(Mockito.isA(Pageable::class.java))
Mockito.verify(smelterMapper , Mockito.times(1)).toDtoPage(Mockito.isA(Page::class.java) as Page<Smelter>, Mockito.isA(Pageable::class.java))
}
}
冶炼厂控制器
package net.app.web
import net.app.service.smelter.SmelterDto
import net.app.service.smelter.SmelterMapper
import net.app.service.smelter.SmelterService
import org.slf4j.LoggerFactory
import org.springdoc.api.annotations.ParameterObject
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.http.MediaType
import org.springframework.transaction.annotation.Transactional
import org.springframework.web.bind.annotation.*
@RestController
@RequestMapping(path = ["/api/smelter/"], produces = [MediaType.APPLICATION_JSON_VALUE])
@CrossOrigin
class SmelterController(private val smelterService: SmelterService,
private val smelterMapper: SmelterMapper) {
companion object {
private val logger = LoggerFactory.getLogger(SmelterController::class.java)
}
/**
* List all smelters
*/
@GetMapping("list")
@Transactional(readOnly = true)
fun listAll(@ParameterObject pageable: Pageable): Page<SmelterDto> {
val entityPage = smelterService.listAll(pageable)
return smelterMapper.toDtoPage(entityPage, pageable)
}
}
SmelterServiceImpl
package net.app.service.smelter
import net.app.domain.Smelter
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
interface SmelterService {
fun listAll(pageable: Pageable): Page<Smelter>
}
@Service
class SmelterServiceImpl(
private val smelterRepository: SmelterRepository
) : SmelterService {
@Transactional(readOnly = true)
override fun listAll(pageable: Pageable): Page<Smelter> {
return smelterRepository.findAll(pageable)
}
}
SmelterRepository
package net.app.service.smelter
import net.app.domain.Grievance
import net.app.domain.Smelter
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.jpa.repository.JpaRepository
interface SmelterRepository : JpaRepository<Smelter, String> {
}
SmelterDto
package net.app.service.smelter
import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonProperty
import net.app.domain.Smelter
import net.app.service.organization.OrganizationMapper
import org.mapstruct.Mapper
import org.mapstruct.Mapping
import org.mapstruct.Mappings
import org.springframework.data.domain.Page
import org.springframework.data.domain.PageImpl
import org.springframework.data.domain.Pageable
class SmelterDto {
@JsonProperty
var id: String? = null
@JsonProperty
var name: String? = null
@JsonProperty
var metal: String? = null
@JsonProperty("location")
var countryLocation: String? = null
@JsonProperty
var lastAuditDate: String? = null
@JsonProperty
var auditCycle: String? = null
@JsonIgnore
var rmapEligibility: String? = null
@JsonIgnore
var rmapAuditStatus: String? = null
@JsonIgnore
var dbLastUpdate: String? = null
@JsonIgnore
var organizationId: Long? = null
}
@Mapper(componentModel = "spring",
uses = [
OrganizationMapper::class
])
abstract class SmelterMapper {
@Mappings(
Mapping(source = "organization.id", target = "organizationId")
)
abstract fun toDto(smelter: Smelter): SmelterDto
abstract fun toDtoList(entityList: List<Smelter>): List<SmelterDto>
fun toDtoPage(entityPage: Page<Smelter>, pageable: Pageable): Page<SmelterDto> {
val dtoList = this.toDtoList(entityPage.content)
return PageImpl(dtoList, pageable, entityPage.totalElements)
}
@Mappings(
Mapping(source = "organizationId", target = "organization.id")
)
abstract fun toEntity(smelterDto: SmelterDto): Smelter
}
如果能帮助我们正确测试 class,我们将不胜感激
整个测试 class 对我来说似乎有点混乱...您正在使用 @EnableSpringDataWebSupport @RunWith(MockitoJUnitRunner::class)
注释。测试控制器的标准方法是使用 @WebMvcTest
注释 - 请参阅 https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-testing-spring-boot-applications-testing-autoconfigured-mvc-tests.
然后你的测试 class 应该看起来像这样:
@WebMvcTest(controllers = [SmelterController::class])
internal class SmelterControllerTest @Autowired constructor(
private val mockMvc: MockMvc,
) {
@MockBean
private lateinit var smelterService: SmelterService
@MockBean
private lateinit var smelterMapper: SmelterMapper
@Test
fun `list all smelters returns one item`() {
// mock behaviour of smelterService.listAll
// mock behaviour of smelterMapper.toDtoPage
// perform mockMvc call
// verify mocks
}
}
我正在尝试使用 mockito 版本 3.7.0 来测试我的 Java 11 Spring Boot Kotlin 控制器。我正在使用 Spring Boot 的 2.4.1 版 我的 pom 导入如下
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.7.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>3.7.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>3.7.0</version>
<scope>test</scope>
</dependency>
当我运行我的测试时,我得到如下错误
java.lang.NullPointerException: Parameter specified as non-null is null: method net.app.service.smelter.SmelterMapper.toDtoPage, parameter entityPage
at net.app.service.smelter.SmelterMapper.toDtoPage(SmelterDto.kt)
at net.app.web.SmelterControllerTest.testListAllSmelters_Success1ItemReturned(SmelterControllerTest.kt:124)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.mockito.internal.runners.DefaultInternalRunner.evaluate(DefaultInternalRunner.java:54)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access0(ParentRunner.java:66)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:99)
at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:105)
at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:40)
at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:163)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53)
,
我想知道测试我的控制器的最佳方法是什么class - 这是我现在所拥有的
SmelterControllerTest
package net.app.web
import net.web.domain.Smelter
import net.web.service.smelter.SmelterDto
import net.web.service.smelter.SmelterMapper
import net.web.service.smelter.SmelterService
import org.junit.jupiter.api.Test
import org.mockito.Mockito
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.boot.test.mock.mockito.MockBean
import org.springframework.data.domain.Page
import org.springframework.data.domain.PageImpl
import org.springframework.data.domain.Pageable
import org.springframework.http.MediaType
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultHandlers
import org.springframework.test.web.servlet.result.MockMvcResultMatchers
import java.util.*
@WebMvcTest(controllers = [SmelterController::class], )
internal class SmelterControllerTest @Autowired constructor(
private val mockMvc: MockMvc,
) {
companion object {
private val BASE_ENDPOINT = "/api/smelter"
}
@MockBean
private lateinit var smelterService: SmelterService
@MockBean
private lateinit var smelterMapper: SmelterMapper
@Test
fun `list all smelters returns one item`() {
val smelterList = LinkedList<Smelter>()
smelterList.add(Smelter())
val smelters = PageImpl(smelterList)
val smelterDtoList = LinkedList<SmelterDto>()
val smelterDto = SmelterDto()
smelterDto.id = "id"
smelterDto.name = "name"
smelterDto.metal = "value"
smelterDtoList.add(smelterDto)
val smelterDtos = PageImpl(smelterDtoList)
Mockito.`when`(smelterService.listAll(Mockito.isA(Pageable::class.java))).thenReturn(smelters)
Mockito.`when`(smelterMapper.toDtoPage(Mockito.isA(Page::class.java) as Page<Smelter>, Mockito.isA(Pageable::class.java))).thenReturn(smelterDtos)
mockMvc.perform(
MockMvcRequestBuilders.get("$BASE_ENDPOINT/list?page=1&size=19&sort=id")
.contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().is2xxSuccessful)
.andExpect(MockMvcResultMatchers.jsonPath("$").exists())
.andExpect(MockMvcResultMatchers.jsonPath("$.content").exists())
.andExpect(MockMvcResultMatchers.jsonPath("$.content[0].id").value(smelterDtoList.get(0).id.toString()))
.andExpect(MockMvcResultMatchers.jsonPath("$.content[0].name").value(smelterDtoList.get(0).name.toString()))
.andExpect(MockMvcResultMatchers.jsonPath("$.content[0].metal").value(smelterDtoList.get(0).metal.toString())).andReturn()
Mockito.verify(smelterService , Mockito.times(1)).listAll(Mockito.isA(Pageable::class.java))
Mockito.verify(smelterMapper , Mockito.times(1)).toDtoPage(Mockito.isA(Page::class.java) as Page<Smelter>, Mockito.isA(Pageable::class.java))
}
}
冶炼厂控制器
package net.app.web
import net.app.service.smelter.SmelterDto
import net.app.service.smelter.SmelterMapper
import net.app.service.smelter.SmelterService
import org.slf4j.LoggerFactory
import org.springdoc.api.annotations.ParameterObject
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.http.MediaType
import org.springframework.transaction.annotation.Transactional
import org.springframework.web.bind.annotation.*
@RestController
@RequestMapping(path = ["/api/smelter/"], produces = [MediaType.APPLICATION_JSON_VALUE])
@CrossOrigin
class SmelterController(private val smelterService: SmelterService,
private val smelterMapper: SmelterMapper) {
companion object {
private val logger = LoggerFactory.getLogger(SmelterController::class.java)
}
/**
* List all smelters
*/
@GetMapping("list")
@Transactional(readOnly = true)
fun listAll(@ParameterObject pageable: Pageable): Page<SmelterDto> {
val entityPage = smelterService.listAll(pageable)
return smelterMapper.toDtoPage(entityPage, pageable)
}
}
SmelterServiceImpl
package net.app.service.smelter
import net.app.domain.Smelter
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
interface SmelterService {
fun listAll(pageable: Pageable): Page<Smelter>
}
@Service
class SmelterServiceImpl(
private val smelterRepository: SmelterRepository
) : SmelterService {
@Transactional(readOnly = true)
override fun listAll(pageable: Pageable): Page<Smelter> {
return smelterRepository.findAll(pageable)
}
}
SmelterRepository
package net.app.service.smelter
import net.app.domain.Grievance
import net.app.domain.Smelter
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.jpa.repository.JpaRepository
interface SmelterRepository : JpaRepository<Smelter, String> {
}
SmelterDto
package net.app.service.smelter
import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonProperty
import net.app.domain.Smelter
import net.app.service.organization.OrganizationMapper
import org.mapstruct.Mapper
import org.mapstruct.Mapping
import org.mapstruct.Mappings
import org.springframework.data.domain.Page
import org.springframework.data.domain.PageImpl
import org.springframework.data.domain.Pageable
class SmelterDto {
@JsonProperty
var id: String? = null
@JsonProperty
var name: String? = null
@JsonProperty
var metal: String? = null
@JsonProperty("location")
var countryLocation: String? = null
@JsonProperty
var lastAuditDate: String? = null
@JsonProperty
var auditCycle: String? = null
@JsonIgnore
var rmapEligibility: String? = null
@JsonIgnore
var rmapAuditStatus: String? = null
@JsonIgnore
var dbLastUpdate: String? = null
@JsonIgnore
var organizationId: Long? = null
}
@Mapper(componentModel = "spring",
uses = [
OrganizationMapper::class
])
abstract class SmelterMapper {
@Mappings(
Mapping(source = "organization.id", target = "organizationId")
)
abstract fun toDto(smelter: Smelter): SmelterDto
abstract fun toDtoList(entityList: List<Smelter>): List<SmelterDto>
fun toDtoPage(entityPage: Page<Smelter>, pageable: Pageable): Page<SmelterDto> {
val dtoList = this.toDtoList(entityPage.content)
return PageImpl(dtoList, pageable, entityPage.totalElements)
}
@Mappings(
Mapping(source = "organizationId", target = "organization.id")
)
abstract fun toEntity(smelterDto: SmelterDto): Smelter
}
如果能帮助我们正确测试 class,我们将不胜感激
整个测试 class 对我来说似乎有点混乱...您正在使用 @EnableSpringDataWebSupport @RunWith(MockitoJUnitRunner::class)
注释。测试控制器的标准方法是使用 @WebMvcTest
注释 - 请参阅 https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-testing-spring-boot-applications-testing-autoconfigured-mvc-tests.
然后你的测试 class 应该看起来像这样:
@WebMvcTest(controllers = [SmelterController::class])
internal class SmelterControllerTest @Autowired constructor(
private val mockMvc: MockMvc,
) {
@MockBean
private lateinit var smelterService: SmelterService
@MockBean
private lateinit var smelterMapper: SmelterMapper
@Test
fun `list all smelters returns one item`() {
// mock behaviour of smelterService.listAll
// mock behaviour of smelterMapper.toDtoPage
// perform mockMvc call
// verify mocks
}
}