如何在 REST 控制器中模拟一个异常,其中该异常是从一个服务中抛出的。

huangapple go评论86阅读模式
英文:

How to mock a exception in rest controller where the exception is thrown from a service

问题

我正在编写 Spring Boot 中的 REST Controller 的测试用例

为了提供一些关于问题的背景我无法在控制器层从服务中抛出的异常进行模拟

我在编写测试用例时使用了 PowerMockito

```java
@RunWith(PowerMockRunner.class)
@PrepareForTest(UserApiController.class)
public class UserApiControllerTest {

   /* private MockMvc mockMvc;*/
    @Mock
    GenericService genericService;

    @Mock
    UserService userService;

    @Mock
    UserRoleService userRoleService;

    @Mock
    MessageService messageService;

    @Mock
    CurrentUserService currentUserService;

    @Mock
    UserRepository userRepository;

    @Mock
    UserRoleRepository userRoleRepository;

    @InjectMocks
    UserApiController userApiController;

    @Before
    public void setup() {
        // this must be called for the @Mock annotations above to be processed
        MockitoAnnotations.initMocks(this);
        //Set up mocking for repository methods
    }

    @Test(expected = DuplicateUserEmailException.class)
    public void saveUserByEmailFailure() throws Exception {
        HashMap mockUser = new HashMap<String,Object>();
        mockUser.put("email","test_dy1@gmail.com");
        String user = new ObjectMapper().writeValueAsString(mockUser);
        doThrow(new DuplicateUserEmailException()).
                when(userService).saveUserByEmailId(any());

        userApiController.saveUserByEmailId(user);
    }

请在下方找到控制器的详细信息。

	@RequestMapping(value = "/save_user_by_email", method = RequestMethod.POST)
	public String saveUserByEmailId(@RequestParam("user") String user) throws Exception {
		ObjectMapper mapper = new ObjectMapper();
		HashMap<String, Object> userJson = new HashMap<String, Object>();
		userJson = mapper.readValue(user, new TypeReference<Map<String, Object>>(){});
		JsonObjectBuilder responseBuilder = Json.createObjectBuilder();
		try {
			userService.saveUserByEmailId(userJson);
			responseBuilder.add("success", true).add("code", genericService.getSuccessCode()).add("message",
					userJson.containsKey("id") ? messageService.getMessage("message.success.update")
							: messageService.getMessage("message.success.create"));
		} catch (Exception exception) {
			LOGGER.error(exception.getMessage(), exception);
			if(exception instanceof DuplicateUserEmailException)
				throw new DuplicateUserEmailException();
		}

		return responseBuilder.build().toString();
	}

<details>
<summary>英文:</summary>

I am working on writing a test cases for rest Controller in Spring boot. 

To give some context about the problem, I am not able to mock the exception that has been thrown from service at the controller layer.

I am using PowerMokito for writing test cases.

```java
@RunWith(PowerMockRunner.class)
@PrepareForTest(UserApiController.class)
public class UserApiControllerTest {


   /* private MockMvc mockMvc;*/
    @Mock
    GenericService genericService;

    @Mock
    UserService userService;

    @Mock
    UserRoleService userRoleService;

    @Mock
    MessageService messageService;

    @Mock
    CurrentUserService currentUserService;

    @Mock
    UserRepository userRepository;

    @Mock
    UserRoleRepository userRoleRepository;

    @InjectMocks
    UserApiController userApiController;


    @Before
    public void setup() {
        // this must be called for the @Mock annotations above to be processed
        MockitoAnnotations.initMocks(this);
        //Set up mocking for repository methods
    }

    @Test(expected = DuplicateUserEmailException.class)
    public void saveUserByEmailFailure() throws Exception {
        HashMap mockUser = new HashMap&lt;String,Object&gt;();
        mockUser.put(&quot;email&quot;,&quot;test_dy1@gmail.com&quot;);
        String user = new ObjectMapper().writeValueAsString(mockUser);
        doThrow(new DuplicateUserEmailException()).
                when(userService).saveUserByEmailId(any());

        userApiController.saveUserByEmailId(user);
    }

Please find the details of the controller below.

	@RequestMapping(value = &quot;/save_user_by_email&quot;, method = RequestMethod.POST)
	public String saveUserByEmailId(@RequestParam(&quot;user&quot;) String user) throws Exception {
		ObjectMapper mapper = new ObjectMapper();
		HashMap&lt;String, Object&gt; userJson = new HashMap&lt;String, Object&gt;();
		userJson = mapper.readValue(user, new TypeReference&lt;Map&lt;String, Object&gt;&gt;(){});
		JsonObjectBuilder responseBuilder = Json.createObjectBuilder();
		try {
			userService.saveUserByEmailId(userJson);
			responseBuilder.add(&quot;success&quot;, true).add(&quot;code&quot;, genericService.getSuccessCode()).add(&quot;message&quot;,
					userJson.containsKey(&quot;id&quot;) ? messageService.getMessage(&quot;message.success.update&quot;)
							: messageService.getMessage(&quot;message.success.create&quot;));
		} catch (Exception exception) {
			LOGGER.error(exception.getMessage(), exception);
			if(exception instanceof DuplicateUserEmailException)
				throw new DuplicateUserEmailException();
		}

		return responseBuilder.build().toString();
	}

答案1

得分: 1

@RunWith(PowerMockRunner.class)
@PrepareForTest(UserApiController.class)
public class UserApiControllerTest {

    private MockMvc mockMvc;
    @Mock
    GenericService genericService;

    @Mock
    UserService userService;

    @Mock
    UserRoleService userRoleService;

    @Mock
    MessageService messageService;

    @Mock
    CurrentUserService currentUserService;

    @InjectMocks
    ApiExceptionHandlerAdvice apiExceptionHandlerAdvice;

    @Mock
    UserRepository userRepository;

    @Mock
    UserRoleRepository userRoleRepository;

    @InjectMocks
    UserApiController userApiController;

    
    @Before
    public void setup(){
        this.mockMvc = MockMvcBuilders.standaloneSetup(userApiController).setControllerAdvice(apiExceptionHandlerAdvice).build();
    }

    @Test
    public void testAddDuplicateCustomer() throws Exception {
        HashMap mockUser = new HashMap&lt;String,Object&gt;();
        mockUser.put(&quot;email&quot;,&quot;test_dy1@gmail.com&quot;);
        String user = new ObjectMapper().writeValueAsString(mockUser);
        when(userService.saveUserByEmailId(any())).thenThrow(DuplicateUserEmailException.class);
        when(messageService.getMessage(any())).thenReturn(&quot;200&quot;);
        RequestBuilder requestBuilder = MockMvcRequestBuilders.post(&quot;/user/api/save_user_by_email&quot;).param(&quot;user&quot;,user);
        MvcResult result = mockMvc.perform(requestBuilder).andReturn();
        MockHttpServletResponse response = result.getResponse();
        assertEquals(HttpStatus.BAD_REQUEST.value(), response.getStatus());
    }
}
英文:

Finally, I solved it. Please find the code below and comment if you have doubts.

@RunWith(PowerMockRunner.class)
@PrepareForTest(UserApiController.class)
public class UserApiControllerTest {


    private MockMvc mockMvc;
    @Mock
    GenericService genericService;

    @Mock
    UserService userService;

    @Mock
    UserRoleService userRoleService;

    @Mock
    MessageService messageService;

    @Mock
    CurrentUserService currentUserService;

    @InjectMocks
    ApiExceptionHandlerAdvice apiExceptionHandlerAdvice;

    @Mock
    UserRepository userRepository;

    @Mock
    UserRoleRepository userRoleRepository;

    @InjectMocks
    UserApiController userApiController;

    
    @Before
    public void setup(){

      //  MockitoAnnotations.initMocks(this);
        this.mockMvc = MockMvcBuilders.standaloneSetup(userApiController).setControllerAdvice(apiExceptionHandlerAdvice).build();
    }

    @Test
    public void testAddDuplicateCustomer() throws Exception {

        HashMap mockUser = new HashMap&lt;String,Object&gt;();
        mockUser.put(&quot;email&quot;,&quot;test_dy1@gmail.com&quot;);
        String user = new ObjectMapper().writeValueAsString(mockUser);
        when(userService.saveUserByEmailId(any())).thenThrow(DuplicateUserEmailException.class);
        when(messageService.getMessage(any())).thenReturn(&quot;200&quot;);
        RequestBuilder requestBuilder = MockMvcRequestBuilders.post(&quot;/user/api/save_user_by_email&quot;).param(&quot;user&quot;,user);
        MvcResult result = mockMvc.perform(requestBuilder).andReturn();
        MockHttpServletResponse response = result.getResponse();
        assertEquals(HttpStatus.BAD_REQUEST.value(), response.getStatus());
        // assertEquals(HttpStatus.BAD_REQUEST.value(), response.getStatus());
    }

答案2

得分: 0

尝试如下方式:

PowerMockito.doThrow(new DuplicateUserEmailException()).
                when(userService).saveUserByEmailId(any(String.class));

确保 saveUserByEmailId 方法的返回类型是 void。

英文:

Try it like this:

PowerMockito.doThrow(new DuplicateUserEmailException()).
when(userService).saveUserByEmailId(any(String.class));

Make sure saveUserByEmailId method is void return type

答案3

得分: 0

可能你没有将那个模拟实例传递给userApiController的真实实例。这是使用模拟服务经常出现的错误。你是在使用 @InjectMocks 吗?还是在手动构造userApiController实例?你能告诉我们你在测试类上面使用的注解是什么吗?

老实说,我建议只使用Mockito,因为使用PowerMock可以模拟许多疯狂的事情,它涉及到字节码操作。

使用Mockito,代码如下:

doThrow(newRuntimeException()).when(paymentService).completePayment(any(), any(), any());

如果你想要模拟网络调用,请使用WireMock。我们已经使用了很长时间的WireMock,它为我们的测试套件增添了很多价值。

英文:

Possible you're not passing that mock instance towards userApiController real instance. That is a really common mistake with mock services. Are you using @InjectMocks for that, or are you constructing userApiController instance manually ? Could you please tell us the annotations that are you using above test class ?

To be honest I'd suggest to use only Mockito since with PowerMock you can mock a lot of crazy things, and it's making byte code manipulation

With Mockito this will look like this:

doThrow(newRuntimeException()).when(paymentService).completePayment(any(), any(), any());

And if you want to mock networking calls, please use WireMock, we've used it for a long time now and it adds a lot of value to our tests pack.

huangapple
  • 本文由 发表于 2020年4月8日 14:46:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/61094787.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定