单元测试或模拟:使用Rest模板在Spring Boot中测试服务类

时间:2020-06-15 18:53:23

标签: spring-boot rest unit-testing service mocking

我有一个服务类,出于可读性考虑,我为代码提供了虚拟变量和对象。我试图主要使用Mockito编写该服务的JUNIT测试类。无论我多么努力,无论使用间谍/模拟,我都无法使用serviceMethod方法。在主要课程之后,我还包括了我编写的测试。

测试通过,没有任何错误,但是无法从实际的类中调用该方法。此后,它不再包含在测试范围内。 (实际类中的方法在Eclipse / SONAR报告中以红色排除)

我知道我在这里遗漏了一些东西,但是并没有想到。我需要检查一下,让我知道如何为此编写适当的测试类并获得该方法的覆盖范围。

(P.S。所有必要的导入均已就位,为了简洁起见,此处未粘贴)

谢谢!

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class ServiceClass {

    private static final Logger LOGGER = LoggerFactory.getLogger(ServiceClass.class);

    @Autowired
    private Config Config;

    @Autowired
    private RestTemplate restTemplate;

    public void serviceMethod(BusinessClass businessObject) {
        String NotificationUrl = Config.getApplicationProperty(businessObject.getCode()).getNotificationUrl();
        HttpEntity<StoreNotify> request = new HttpEntity<>(businessObject);
        ResponseEntity<String> response = restTemplate.exchange(NotificationUrl, HttpMethod.POST, request,
                String.class);

        LOGGER.info("Service call, response: {} and status: {} ", response.getBody(),
                response.getStatusCode());
    }
}

上述测试类,

import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatchers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

@RunWith(MockitoJUnitRunner.Silent.class)
public class ServiceClassTest {

    @InjectMocks
    private ServiceClass serviceClass;

    @Mock
    private RestTemplate restTemplate;

    @InjectMocks
    private BusinessClass businessObject;

    @Test
    public void testServiceMethod() {

        ServiceClass spy = Mockito.spy(serviceClass);
        doNothing().when(spy).serviceMethod(businessObject);
        spy.serviceMethod(businessObject);
        verify(spy, times(1)).serviceMethod(businessObject);

        ResponseEntity<String> responseEntity = new ResponseEntity<>(HttpStatus.ACCEPTED);
        Mockito.when(restTemplate.exchange(ArgumentMatchers.anyString(), ArgumentMatchers.any(HttpMethod.class),
                ArgumentMatchers.<HttpEntity<BusinessClass>>any(), ArgumentMatchers.<Class<String>>any()))
                .thenReturn(responseEntity);

    }

}

2 个答案:

答案 0 :(得分:0)

您可以尝试以下代码。如果失败,您可能需要进行一些更改,如果下面的代码不起作用,请仅注释您的结果。

@RunWith(MockitoJUnitRunner.Silent.class)
public class ServiceClassTest {

    @InjectMocks
    private ServiceClass serviceClass;

    @Mock
    private RestTemplate restTemplate;

    @Mock
    private Config Config;

    @Test
    public void testServiceMethod() { 

    BusinessClass businessObject = new BusinessClass();
    businessObject.setCode("123"); // set as per your datatype

    ResponseEntity<String> responseEntity = new ResponseEntity<>("my String mock body", HttpStatus.ACCEPTED);
    Mockito.when(restTemplate.exchange(Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any()))
                .thenReturn(responseEntity);

    serviceClass.serviceMethod(businessObject);

    verify(serviceClass, times(1)).serviceMethod(Mockito.any(BusinessClass.class));
  }
}

答案 1 :(得分:0)

为了简单起见,我将添加以下测试类,它确实足以满足您的情况。

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.web.client.RestTemplate;

import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class ServiceClassTest {
    private static final String CODE = "code";
    private static final String NOTIFICATION_URL = "notificationUrl";
    @Mock
    private Config config;
    @Mock
    private RestTemplate restTemplate;
    @InjectMocks
    private ServiceClass serviceClass;
    @Mock
    private BusinessClass businessObject;
    @Mock
    private Notification notification;

    @Test
    public void serviceMethod() {
        when(businessObject.getCode()).thenReturn(CODE);
        when(config.getApplicationProperty(CODE)).thenReturn(notification);
        when(notification.getNotificationUrl()).thenReturn(NOTIFICATION_URL);

        serviceClass.serviceMethod(businessObject);

        verify(restTemplate, times(1)).exchange(NOTIFICATION_URL, HttpMethod.POST, new HttpEntity<>(businessObject), String.class);
    }
}

如果我要简要说明此测试方法的作用

  • 前3行:做必要的模拟,以确保我们的测试方法仅取决于ServiceClass中的代码。因此,在第1行中:我确保businessObject.getCode()返回我想要的CODE。请注意,businessObject是我们要传递到测试方法内的ServiceClass.serviceMethod(BusinessClass businessObject)的参数。现在我知道,无论是哪种情况,businessObject.getCode()都会返回CODE,这样,当我调用ServiceClass.serviceMethod时,在我的Config.getApplicationProperty(String code)(原始类)中,CODE将通过作为参数。现在在第2行中:我建议每当将CODE作为config.getApplicationProperty(String code)的参数传递时,返回我想要的notification。在第3行中:我建议notification.getNotificationUrl()依次返回NOTIFICATION_URL
  • 调用我们要测试的方法。请注意,我们不应嘲笑此方法。
  • 由于这是一个void方法,因此我使用Mockito.verify来确保执行是按照我的模拟安排和实际(原始)类逻辑内联进行的。在这里,我要验证的是执行的代码称为restTemplate的方式。

正如我在一开始提到的,我上面写的是一个简单的测试,可以满足您的要求。消化完这些之后,您可以看看mock-server,这可能对您进行测试休息电话很有帮助。