我需要使用Spring,Junit 5,Mockito,REST-Assured和Lombok设置控制器测试的帮助

问题描述

我需要使用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();
    }
...
}