java - 使用 junit 和 mockito 在 java ee 应用程序中通过 Rest api 调用测试服务

标签 java rest unit-testing junit mockito

我正在尝试使用 JUnit 和 Mockito 测试服务类。不确定如何在测试方法中包含 HTTP GET 请求 URI。我正在尝试使用 HttpClientBuilder 创建和构建请求,但不知何故,即使在包含 Gradle 依赖项后也找不到导入。我在请求中也有 header 参数。

我的Java类如下:

@GET
@Path("/home")
public Response getAllEmployees(@HeaderParam("user") String user, @HeaderParam("password") String password) {
    List<Info> listOfInfo = new ArrayList<>();
    LoginUser loginUser = checkLoginValidation();
    
    if(loginUser != null){
        List<Employees> list = employeeManager.fetchAllEmployees();

        if(list == null || list.isEmpty()){
            return Response.status(Status.NO_CONTENT).build();
        }
        
        listOfInfo = getEmployees(list);
        log.info(listOfInfo);
        return Response.ok(listOfInfo, MediaType.APPLICATION_JSON_TYPE).build();
    }
    
    return Response.status(Status.BAD_REQUEST).build();
}

这就是我在下面的测试类中编写的全部内容:

@RunWith(MockitoJUnitRunner.class)
public class FinderServiceTest {

    @InjectMocks
    private FinderService finderService;

    @Test
    public void testMethod() {
        HttpGet request = new HttpGet("http://localhost:8080/home");
    
        //Don't know how to proceed further
    }
}

任何帮助将不胜感激!

最佳答案

您的问题的措辞方式我担心您正在混合单元和集成级别测试。

在单元级别,您希望断言当受控参数传递给方法时,您可以预测地断言该方法将做什么并返回给客户端。这是一个隔离测试,将该方法与其执行环境分开,并专注于确保一小部分代码的可预测行为。

在集成级别,您将“按照运行时存在的方式”对待该类。您可以将其放入容器中,启动 bean 或服务,并对其进行调用,以确保它运行时的行为同样可预测,并且外部客户端将通过完整的运行堆栈从公众获得您期望的结果(或受控堆栈子集)。集成测试正在检查应用程序是否按照您的预期整体运行。

通过您提出的测试,我觉得您正在尝试执行该方法的单元测试。我建议不要担心运行时如何从 Header 派生参数,而直接调用该方法。

下面是我相信如何尝试对 ServiceFinder.getAllLockers(String, String) 方法进行单元测试的 stub

import static org.junit.Assert.assertEquals;

import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.internal.util.reflection.Whitebox;

public class FinderServiceTest {
    // This is created new between each test.
    private ServiceFinder serviceFinder = new ServiceFinder();
/* ***************************************/
/*  PARAMETER TESTS
/* ***************************************/
/*
 * What should happen in these cases? - Is it an Exception? - Should the method return Status.BAD_REQUEST?
 * 
 * org.junit.Assert can be used to compare the response Object with the desired outcome OR
 * org.junit.rules.ExpectedException can be used to verify specific exceptions are thrown
 */
@Test
public void testGetAllLockersNullUserParam() {
    Response response = serviceFinder.getAllLockers(null, "password");

    // Assert the right thing happened.
}

@Test
public void testGetAllLockersEmptyUserParam() {
    Response response = serviceFinder.getAllLockers("", "password");

    // Assert the right thing happened.
}

@Test
public void testGetAllLockersNullPasswordParam() {
    Response response = serviceFinder.getAllLockers("user", null);

    // Assert the right thing happened.
}

@Test
public void testGetAllLockersEmptyPasswordParam() {
    Response response = serviceFinder.getAllLockers("user", "");

    // Assert the right thing happened.
}

/* ***************************************/
/*  BRANCH TESTS (SHORT PATHS)
/* ***************************************/

@Test
public void testGetAllLockersNullValidatedUser() {
    // For ease of use in my case I'm going to pretend that checkLoginValidation 
    // just calls a delegate interface, which I'm calling LoginValidator, with the same API.
    // Which I will mock and set the expected return...
    LoginValidator mockLoginValidator = Mockito.mock(LoginValidator.class);
    Mockito.when(mockLoginValidator.checkLoginValidation()).thenReturn(null);
    //Using PowerMock, I'm going to set the LoginValidator field inside of my service finder.
    //I'm assuming that LoginValidator field in the ServiceFinder is named "loginValidator"
    Whitebox.setInternalState(serviceFinder, "loginValidator", mockLoginValidator);

    //Now that my class is set up to give me the null Validated User, I'll make the call to the test instance
    Response response = serviceFinder.getAllLockers("validUser", "validPassword");

    //From the implementation posted, I know that when the user validates as null I should get back something with a Status.BAD_REQUEST state.
    assertEquals("When the logged in user is null BAD_REQUEST should be returned", Status.BAD_REQUEST, response.getStatus);   
}

@Test
public void testGetAllLockersNullEmployeeList() {
    //FIXME:  Configure user validation to return LoginUser Object.
    //FIXME:  Configure test reference state to return a null employee list when employeeManager.fetchAllEmployees() is called

    Response response = serviceFinder.getAllLockers("validUser", "validPassword");
    assertEquals("When the employee list is null NO_CONTENT should be returned", Status.NO_CONTENT, response.getStatus);   
}

@Test
public void testGetAllLockersEmptyEmployeeList() {
  //FIXME:  Configure user validation to return LoginUser Object.
    //  FIXME:  Configure test reference state to return an empty employee list when employeeManager.fetchAllEmployees() is called

    Response response = serviceFinder.getAllLockers("validUser", "validPassword");
    assertEquals("When the employee list is null NO_CONTENT should be returned", Status.NO_CONTENT, response.getStatus);
}

/* ***************************************/
/*  HAPPY PATH TEST
/* ***************************************/
public void testgetAllLockers() {
  //FIXME:  Configure user validation to return LoginUser Object.
    //  FIXME:  Configure test reference state to return a correctly-populated employee list when employeeManager.fetchAllEmployees() is called

    Response response = serviceFinder.getAllLockers("validUser", "validPassword");
    assertEquals("When the employee list is null NO_CONTENT should be returned", Status.OK, response.getStatus);
    //FIXME get JSON from response reference
    //FIXME Check that JSON holds all of the expected employee list data
}

}

在单元级别确保该方法重复执行我们期望的操作。

<小时/>

集成测试在我看来完全是另一头野兽。您需要让代码在服务器上运行,并设置一个可以针对正在运行的 URL 进行调用的工具。该配置不是我太熟悉的,所以我在这方面没有太大帮助。我确实知道有很多方法可以做到这一点,并且有很多程序和实用程序可以提供帮助。在谷歌上搜索一些关于集成测试 REST API 的内容,我相信您会有很多选择。我建议您寻找一种与您最终打算运行的环境非常相似的解决方案。

我可以提供的是注意,在集成测试期间,您将需要使用HttpClientBuilder之类的东西,或者JMeterPostman之类的工具 向服务器发出信息请求,然后读取并验证响应是否符合您的预期。您可能想要使用我们在单元测试中尝试过的一些数据,以确保正在运行的系统不会改变预期结果。

祝你好运!

关于java - 使用 junit 和 mockito 在 java ee 应用程序中通过 Rest api 调用测试服务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51322267/

相关文章:

java - 查找字符串的所有不同子串

java - 在 Unix 上使用 Java 验证 SSH 凭据

java - 为 secure spring petclinic 的每个用户单独的 mysql 登录

java - 使用 Mockito 测试涉及数据库调用的 Rest API

json - Angular - FormGroup 显示来自服务器 Rest Api 的 json 错误

c# - 如何在单元测试中验证 Flurl Http 中的请求正文内容?

java - 如何使用 for 循环制作一堆 10 个荞麦华夫饼?

java - Jersey REST 服务的响应不包含空字段

asp.net-mvc - 单元测试 RedirectToRouteResult

Django 测试 : determine which view was executed