【问题标题】:Spock testing for SpringBoot RestController and mocking of exceptionsSpock 测试 SpringBoot RestController 和模拟异常
【发布时间】:2018-03-09 17:53:21
【问题描述】:

我不熟悉为 Spring Boot 应用程序测试 RestController 的常规方法。我有一个控制器类:

@RestController
@RequestMapping(value = "/onboarding/v1")
public class OnboardingController {
  private static final Logger LOG = LoggerFactory.getLogger(OnboardingServiceImpl.class);

  @Autowired
  private OnboardingService onboardingService;

  @RequestMapping(
    value = "/service-models",
    method = RequestMethod.POST,
    consumes = { "multipart/form-data" },
    produces = { "application/json" }
  )
  public ResponseEntity createServiceModel(
    @RequestParam("name") final String name,
    @RequestParam("file") final MultipartFile file
  ){
    try {
      final ServiceModelRequestData serviceModelRequestData =
        new ServiceModelRequestData(name, file);
      final ServiceModelDetail createdServiceModel =
        onboardingService.createServiceModel(serviceModelRequestData);
      return new ResponseEntity<>(createdServiceModel, HttpStatus.OK);
    }
    catch (MalformedContentException ex) {
      LOG.error("Malformed Content:", ex);
      return new ResponseEntity<>(errorMessage(ex), HttpStatus.BAD_REQUEST);
    }
    catch (ServiceModelNameAlreadyExistsException ex) {
      LOG.error("Service Model Name Already Exists:", ex);
      return new ResponseEntity<>(errorMessage(ex), HttpStatus.CONFLICT);
    }
    catch (ServiceUnavailableException ex) {
      LOG.error("Service Unavailable currently:" + ex);
      return new ResponseEntity<>(errorMessage(ex), HttpStatus.SERVICE_UNAVAILABLE);
    }
  }
  //...
}

我无法找到如何对上述类进行 spock 测试以及编写异常的 spock 测试用例并检查所需的响应?我想创建一个测试用例,它在调用方法时抛出错误并返回响应实体我要检查的包含给定的 Jason 数据以及所需的 Http 状态。非常感谢代码 sn-p 的示例 spock 测试。

已编辑: 测试类如下(以及我正在尝试做的 cmets):

package com.service.onboarding.web.controller

import spock.lang.Specification

import com.service.onboarding.business.OnboardingServiceImpl
import com.service.onboarding.business.api.OnboardingService
import com.service.onboarding.domain.exception.MalformedContentException
import com.service.onboarding.domain.exception.ServiceModelNameAlreadyExistsException
import com.service.onboarding.domain.exception.ServiceUnavailableException
import com.service.onboarding.domain.resource.Greeting

import groovy.json.internal.Exceptions

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.test.web.servlet.MockMvc
import org.springframework.web.multipart.MultipartFile
import org.springframework.boot.test.mock.mockito.MockBean
import org.springframework.http.ResponseEntity
import spock.lang.Unroll
import static org.mockito.BDDMockito.given



/*@WebMvcTest
 public class OnboardingControllerSpec extends Specification {
 @MockBean
 private OnboardingService onboardingService;
 @Autowired
 private MockMvc mockMvc;
 def "controller should return expected JSON content and OK response"() {
 given: 'hello world service responds with greeting'
 def name = "Emily"
 given(onboardingService.getPersonalGreeting("${name}")).willReturn(new Greeting(1, "Hi, ${name}"));
 when: 'hello world service is called with name provided'
 def response  = mockMvc.perform(get("/onboarding/v1?name=${name}"))
 then: 'expected JSON returned and response code is OK'
 response
 .andExpect(status().isOk())
 .andExpect(content().json("{'id': 1, 'content': 'Hi, ${name}'}"))
 }
 }*/

public class OnboardingControllerSpec extends Specification{
    OnboardingServiceImpl service =new OnboardingServiceImpl()
    OnboardingController controller
    ResponseEntity response

    @Unroll
    def "HTTP response #statusCode when creating service model"() {
        given:
        if (exception) {
            service = Stub() {
                createServiceModel(_) >> { throw exception }
            }
        }

        controller= new OnboardingController(onboardingService: service)

        when:
        response=controller.createServiceModel("test", Mock(MultipartFile))

        then:
        response.statusCode == statusCode

        where:
        exception                                    | statusCode
        null                                         | OK
        new MalformedContentException()              | BAD_REQUEST
        new ServiceModelNameAlreadyExistsException() | CONFLICT
        new ServiceUnavailableException()            | SERVICE_UNAVAILABLE

    }
}

我的服务类如下:

package com.service.onboarding.business;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.service.onboarding.business.api.OnboardingService;
import com.service.onboarding.business.servicemanagement.ServiceModelRepository;
import com.service.onboarding.domain.exception.MalformedContentException;
import com.service.onboarding.domain.exception.ServiceModelDoesNotExistException;
import com.service.onboarding.domain.exception.ServiceModelInUseException;
import com.service.onboarding.domain.exception.ServiceModelNameAlreadyExistsException;
import com.onboarding.domain.exception.ServiceUnavailableException;
import com.service.onboarding.domain.requestdata.ServiceModelPaginationFilter;
import com.service.onboarding.domain.requestdata.ServiceModelRequestData;
import com.service.onboarding.domain.resource.Greeting;
import com.service.onboarding.domain.resource.ServiceModel;
import com.service.onboarding.domain.resource.ServiceModelDetail;
import com.service.onboarding.domain.responsedata.PaginatedServiceResponseData;

/*
 * Sample service to demonstrate what the API would use to get things done
 */
@Service
public class OnboardingServiceImpl implements OnboardingService {

    private final AtomicLong counter = new AtomicLong();

    private static final String TEMPLATE = "Hello, %s!";

    private static final Logger LOG = LoggerFactory.getLogger(OnboardingServiceImpl.class);

    @Autowired
    private ServiceModelRepository serviceModelRepository;


    public OnboardingServiceImpl() {
    }

    @Override
    public Greeting getPersonalGreeting(final String name) {
        return new Greeting(counter.incrementAndGet(),
                String.format(TEMPLATE, name));
    }
 @Override
    public ServiceModelDetail createServiceModel(final ServiceModelRequestData serviceModelRequestData) throws MalformedContentException, ServiceModelNameAlreadyExistsException, ServiceUnavailableException {
        return serviceModelRepository.create(serviceModelRequestData);
    }

【问题讨论】:

标签: java spring spring-boot groovy spock


【解决方案1】:

您的测试应如下所示:

package de.scrum_master.stackoverflow

import org.springframework.http.ResponseEntity
import org.springframework.web.multipart.MultipartFile
import spock.lang.Specification
import spock.lang.Unroll

import static org.springframework.http.HttpStatus.*

class OnboardingControllerTest extends Specification {
  OnboardingService service = new OnboardingService()
  OnboardingController controller
  ResponseEntity response

  @Unroll
  def "HTTP response #statusCode when creating service model"() {
    given:
    if (exception) {
      service = Stub() {
        createServiceModel(_) >> { throw exception }
      }
    }
    controller = new OnboardingController(onboardingService: service)

    when:
    response = controller.createServiceModel("test", Mock(MultipartFile))

    then:
    response.statusCode == statusCode

    where:
    exception                                    | statusCode
    null                                         | OK
    new MalformedContentException()              | BAD_REQUEST
    new ServiceModelNameAlreadyExistsException() | CONFLICT
    new ServiceUnavailableException()            | SERVICE_UNAVAILABLE
  }
}

控制台日志(我在复制您的用例时切换到 Java 日志记录)将是:

Mär 10, 2018 12:51:50 PM de.scrum_master.stackoverflow.OnboardingController createServiceModel

INFORMATION: Malformed Content: de.scrum_master.stackoverflow.MalformedContentException
Mär 10, 2018 12:51:50 PM de.scrum_master.stackoverflow.OnboardingController createServiceModel
INFORMATION: Service Model Name Already Exists: de.scrum_master.stackoverflow.ServiceModelNameAlreadyExistsException
Mär 10, 2018 12:51:50 PM de.scrum_master.stackoverflow.OnboardingController createServiceModel
INFORMATION: Service Unavailable currently: de.scrum_master.stackoverflow.ServiceUnavailableException

在我的 IDE (IntelliJ IDEA) 中,测试结果如下所示:


附注:

我知道您是 SO 新手。所以这是你的免费拍摄。下次请提供MCVE,即一个最小的、可编译的和可执行的示例,而不仅仅是没有包名、导入和测试类的代码sn-p。您需要 Spock 方面的帮助,因此请编写一份 Spock 规范(测试类),将其展示给我们并解释您的问题。这样我们就不需要从头开始重新创建您的情况,而可以专注于修复您的测试并解决您的问题。

你很幸运,我只是觉得有点无聊,等待客人到来,否则我不会那样做。说“我一无所有,请为我做一切”,只能说明你很懒。如果您需要帮助,请让其他人轻松帮助并尊重他们的时间预算。他们是免费的!

【讨论】:

  • 请原谅我犯了这个错误。从现在开始,这不会重复,我会确保更准确。仍然习惯这个:( ...但非常感谢你的帮助,我非常感谢你的姿态。
  • 那么我也非常感谢您接受并支持我的回答。
猜你喜欢
  • 2014-05-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-25
  • 1970-01-01
  • 2013-10-11
  • 2014-11-05
  • 1970-01-01
相关资源
最近更新 更多