问题描述
我需要使用Spring,Junit 5,Mockito,REST-Assured和Lombok设置控制器测试的帮助。
我错过了配置步骤吗?
当我尝试运行测试时,我得到: java.lang.NoSuchMethodError:org.mockito.internal.configuration.plugins.Plugins.getMockitoLogger()Lorg / mockito / plugins / MockitoLogger;
在UserController.java中,通过Lombok使用@ Slf4j来设置日志记录,并具有一些log.info(“ Some Log text”)语句。
UserControllerTest.java
@ExtendWith(MockitoExtension.class)
public class UserControllerTest extends TestAbstract {
private final static String URI = "/user";
@Mock
private UserService userService;
@InjectMocks
private UserController userController;
@InjectMocks
private ControllerExceptionHandler controllerExceptionHandler;
@BeforeEach
public void initialiseRestAssuredmockmvcStandalone() {
RestAssuredmockmvc.standalonesetup(userController,controllerExceptionHandler);
}
...
}
Maven Pom:
...
<properties>
<java.version>1.8</java.version>
<junit-jupiter.version>5.6.2</junit-jupiter.version> <!--junit 5-->
<mockito-junit-jupiter.version>3.4.3</mockito-junit-jupiter.version> <!--mockito for junit 5-->
<!--mockito ver 3.5.13 also attempted-->
<spring-mock-mvc.version>3.3.0</spring-mock-mvc.version> <!--rest-assured-->
<maven-surefire-plugin.version>2.22.0</maven-surefire-plugin.version>
</properties>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
<exclusion>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>${mockito-junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>spring-mock-mvc</artifactId>
<version>${spring-mock-mvc.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
...
更新/更正
更正:与junit 5一样,UserControllerTest.java中的@Before
已更正为@BeforeEach
。
更新:如REST-Assured docs所述,使用Spring需要仔细使用导入。
您应该使用:
import static io.restassured.module.mockmvc.RestAssuredmockmvc.*
import static io.restassured.module.mockmvc.matcher.RestAssuredmockmvcMatchers.*
并避免使用:
io.restassured.RestAssured.*
io.restassured.matcher.RestAssuredMatchers.*
更正 更正:REST保证版本为3.3.0。更高版本不适用于Java 8。
解决方法
这是控制器测试问题的解决方法。返回到junit 4进行控制器测试。我的依赖关系没有变化,因为在junit 5中可以访问junit 4。
有趣的是,我发现我可以为集成测试和控制器测试使用相同的REST-Assured代码。唯一的区别是控制器测试模拟了服务层。因此,我有一个抽象的父类UserControllerTestAbstract.java,它带有两个孩子UserControllerTest.java和UserIntegrationTest.java。
UserControllerTestAbstract.java(junit 5)
...
import org.junit.jupiter.api.Test; // junit 5
import static io.restassured.module.mockmvc.RestAssuredMockMvc.given;
...
public abstract class UserControllerTestAbstract extends UserTestAbstract implements ControllerTestAbstract {
... // protected static final User variables ... for testing
@Tag("positive")
@Test
public void whenAddUser_thenReturnsSuccessfulDefaultResponse() {
// given a AddUserRequest
AddUserRequest testAddUserRequest = AddUserRequest.builder()
.user(testNewUser)
.build();
DefaultResponse foundDefaultResponse = given().log().all()
.spec(requestSpec)
.body(testAddUserRequest)
// when addUser returns a Successful DefaultResponse
.when()
.post("/add")
// then the JSON response is as expected
.then().log().all()
.spec(responseSpec)
.extract().as(DefaultResponse.class);
assertSuccessfulStatus(foundDefaultResponse); // custom assert method
}
...
}
当我尝试在抽象类中运行测试时,IntelliJ会问我要使用哪个子类。
ControllerTestAbstract接口是另一个抽象层,定义了requestSpec和responseSpec变量。
UserIntegrationTest.java(junit 5)
import org.junit.jupiter.api.Test; // junit 5
...
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = RANDOM_PORT)
@Tag("integration-test")
public class UserIntegrationTest extends UserControllerTestAbstract {
@Autowired
private WebApplicationContext webApplicationContext;
@BeforeEach // junit 5
public void initialiseRestAssuredMockMvcWebApplicationContext() {
RestAssuredMockMvc.webAppContextSetup(webApplicationContext);
RestAssuredMockMvc.basePath = "/user";
}
// The tests are in UserControllerTestAbstract and are used in the controller test as well.
@Tag("negative")
@Test
@Disabled
public void whenGetListOfAllUsers_thenReturnsErrorUserListResponse() {
// This overrides and disables this integration test.
// The test fails,because data.sql adds users to the DB.
// To run this test,comment out all inserts to the users table in data.sql in both test and main resources.
// Alternatively,you could rename data.sql in both test and main resources.
// Then comment out @Disabled
super.whenGetListOfAllUsers_thenReturnsErrorUserListResponse();
}
}
到目前为止,只需要覆盖和禁用一个测试,因为它需要一个表为空才能生成正确的响应。其他许多测试都希望该表可以被填充。
UserControllerTest.java(junit 4)
import org.junit.Test; // junit 4
...
@RunWith(MockitoJUnitRunner.class) // junit 4
@Tag("controller")
@Tag("unit-test")
public class UserControllerTest extends UserControllerTestAbstract {
@Mock
private UserService userService;
@InjectMocks
private UserController userController;
@InjectMocks
private ControllerExceptionHandler controllerExceptionHandler;
@Before // junit 4
public void initialiseRestAssuredMockMvcStandalone() {
RestAssuredMockMvc.standaloneSetup(userController,controllerExceptionHandler);
RestAssuredMockMvc.basePath = "/user";
}
@Tag("positive")
@Test
public void whenAddUser_thenReturnsSuccessfulDefaultResponse() {
// given a User,a AddUserRequest and a DefaultResponse
AddUserRequest testAddUserRequest = AddUserRequest.builder()
.user(testNewUser)
.build();
DefaultResponse testDefaultResponse = new DefaultResponse();
// when addUser returns a Successful DefaultResponse
given(userService.addUser(any(AddUserRequest.class))).willReturn(testDefaultResponse);
// then the JSON response is as expected
super.whenAddUser_thenReturnsSuccessfulDefaultResponse();
}
...
}