我有一个简单的Spring MVC应用程序,其中包含NullPointerException
的控制器建议类。此控制器建议使用注入@Autowired
的消息源。
我通过使用@MockBean
模拟MessageSource字段来编写对此控制器建议的测试,但看起来Spring引入了默认的DelegatingMessageSource
。
相比之下,SomeService
mock被正确注入。
有人知道为什么MessageSource
无法注入此处,注入它的正确方法是什么?
的build.gradle:
group 'exceptions'
version '1.0-SNAPSHOT'
buildscript {
repositories {
jcenter()
maven { url 'http://repo.spring.io/snapshot' }
maven { url 'http://repo.spring.io/milestone' }
}
dependencies {
classpath 'org.springframework.boot:spring-boot-gradle-plugin:2.0.0.BUILD-SNAPSHOT'
}
}
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'application'
apply plugin: 'idea'
sourceCompatibility = 1.8
repositories {
jcenter()
maven { url "http://repo.spring.io/snapshot" }
maven { url "http://repo.spring.io/milestone" }
}
dependencies {
compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '1.5.10.RELEASE'
compile("org.springframework:spring-test:5.0.2.BUILD-SNAPSHOT")
testCompile("org.springframework.boot:spring-boot-starter-test")
testCompile group: 'junit', name: 'junit', version: '4.12'
}
MyError.java:
package mainpackage.application;
import com.fasterxml.jackson.annotation.JsonProperty;
public class MyError {
@JsonProperty("message")
public String message;
public MyError(String message) {
this.message = message;
}
}
MyExceptionHandler.java:
package mainpackage.application;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import java.util.Locale;
@ControllerAdvice
public class MyExceptionHandler {
private final MessageSource messageSource;
private final SomeService someService;
@Autowired
public MyExceptionHandler(MessageSource messageSource, SomeService someService) {
this.messageSource = messageSource;
this.someService = someService;
}
private static final String MY_KEY = "key";
@ExceptionHandler(NullPointerException.class)
public ResponseEntity<MyError> handleNullPointerException(NullPointerException e) {
String details = someService.getDetails();
return new ResponseEntity<>(new MyError(messageSource.getMessage(MY_KEY, null, Locale.ENGLISH), details), HttpStatus.BAD_REQUEST);
}
}
MyExceptionHandlerTest.java:
package mainpackage.application;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.MessageSource;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Locale;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(SpringRunner.class)
@WebMvcTest(MyExceptionHandler.class)
public class MyExceptionHandlerTest {
@Autowired
private MyExceptionHandler myExceptionHandler;
@MockBean
private MessageSource messageSource;
@MockBean
private SomeService someService;
@Test
public void handleNullPointerException() {
String key = "key";
String message = "message";
String anotherMessage = "anotherMessage";
when(someService.getDetails()).thenReturn(anotherMessage);
when(messageSource.getMessage(key, null, Locale.ENGLISH)).thenReturn(message);
ResponseEntity<MyError> resp = myExceptionHandler.handleNullPointerException(new NullPointerException("something"));
verify(someService).getDetails();
verify(messageSource).getMessage(key, null, Locale.ENGLISH);
assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
assertThat(resp.getBody().message).isEqualTo(message);
}
}
输出: org.springframework.context.NoSuchMessageException:在代码'key'下找不到locale'en'的消息。
at org.springframework.context.support.DelegatingMessageSource.getMessage(DelegatingMessageSource.java:76)
at mainpackage.application.MyExceptionHandler.handleNullPointerException(MyExceptionHandler.java:21)
at mainpackage.application.MyExceptionHandlerTest.handleNullPointerException(MyExceptionHandlerTest.java:34)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:73)
at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:83)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
我将字段依赖项更改为构造函数依赖项。更清楚的是注入了默认的MessageSource
。