Spring Boot MVC Test - MockMvc is always null - java

I'm trying to write my first Spring MVC test but I just cannot get Spring Boot to inject the MockMvc dependency into my test class. Here is my class:
#WebMvcTest
public class WhyWontThisWorkTest {
private static final String myUri = "uri";
private static final String jsonFileName = "myRequestBody.json";
#Autowired
private MockMvc mockMvc;
#Test
public void iMustBeMissingSomething() throws Exception {
byte[] jsonFile = Files.readAllBytes(Paths.get("src/test/resources/" + jsonFileName));
mockMvc.perform(
MockMvcRequestBuilders.post(myUri)
.content(jsonFile)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(MockMvcResultMatchers.status().is2xxSuccessful());
}
}
I've checked with IntelliJ's debugger and can confirm that mockMvc itself is null. Thus, all the Exception message tells me is "java.lang.NullPointerException".
I've already tried adding more general Spring Boot annotations for test classes like "#SpringBootTest" or "#RunWith(SpringRunner.class)" in case it has something to do with initializing Spring but no luck.

Strange, provided that you have also tried with #RunWith(SpringRunner.class) and #SpringBootTest. Have you also tried with the #AutoConfigureMockMvc annotation? The sample below is working fine.
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class HelloControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void getHello() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().string(equalTo("Hello World of Spring Boot")));
}
}
complete sample here
It may also be worthwhile to consider the following comments regarding the usage of the #WebMvcTest and #AutoConfigureMockMvc annotations as detailed in Spring's documentation
By default, tests annotated with #WebMvcTest will also auto-configure Spring Security and MockMvc (include support for HtmlUnit WebClient and Selenium WebDriver). For more fine-grained control of MockMVC the #AutoConfigureMockMvc annotation can be used.
Typically #WebMvcTest is used in combination with #MockBean or #Import to create any collaborators required by your #Controller beans.
If you are looking to load your full application configuration and use MockMVC, you should consider #SpringBootTest combined with #AutoConfigureMockMvc rather than this annotation.
When using JUnit 4, this annotation should be used in combination with #RunWith(SpringRunner.class).

I faced the same issue. It turned out that MockMvc was not injected due to incompatible #Test annotation. The import was org.junit.Test, but changing it to org.junit.jupiter.api.Test solved the issue.

Accepted answer works, however I solved the issue also without importing #RunWith(SpringRunner.class).
In my case I had imported org.junit.Test instead of the newer org.junit.jupiter.api.Test.
If you are using Maven you can avoid to make this mistake excluding junit-vintage-engine from spring-boot-starter-test dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>

check if you have the latest or at least updated maven-surefire-plugin.
It helped me to solve the issue

Related

#WebMvcTest creating more than one Controller for some reason

I'm trying to create a controller test with #WebMvcTest, and as I understand, when I put #WebMvcTest(ClientController.class) annotation of the test class it should not create a whole lot of beans, but just ones that this controller requires.
I'm mocking the bean this controller requires with #MockBean, but somehow it fails with an exception that there's 'No qualifying bean' of another service that does not required by this controller but by another.
So this test is failing:
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = ClientController.class)
public class ClientControllerTest {
#MockBean
ClientService clientService;
#Test
public void getClient() {
assertEquals(1,1);
}
}
I've created an empty Spring Boot project of the same version (2.0.1) and tried to create test over there. It worked perfectly.
So my problem might be because of the dependencies that my project has many, but maybe there's some common practice where to look in this situation? What can mess #WebMvcTest logic?
I've found a workaround. Not to use #WebMvcTest and #MockBean, but to create everything by hand:
//#WebMvcTest(ClientController.class)
#RunWith(SpringRunner.class)
public class ClientControllerTest {
private MockMvc mockMvc;
#Mock
ClientService clientService;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(
new ClientController(clientService)
).build();
}
works with Spring 1.4.X and with Spring Boot 2.X (had different exception there and there), but still doesn't explain why #WebMvcTest doesn't work

Spring Boot with spring-boot-starter-data-jpa in unit test needs mandatory #DataJpaTest

I'm testing Spring Boot capabilities as a newbie in this area. I have a simple app with basic dependencies.
sping-boot-starter-parent 1.5.7
sping-boot-starter
sping-boot-starter-data-jpa
sping-boot-starter-test
Then there is simple Application class with #SpringBootApplication annotation.
Then I have a simple DummyService with #Service annotation.
Then I have created a simple test DummyServiceTest with one #Test method and #RunWith(SpringRunner.class) and #SpringBootTest annotations.
#SpringBootTest is the key problem. With spring-boot-starter-data-jpa dependency and this annotation test requires even #DataJpaTest annotation. Without it the framework doesn't solve HibernateJpaAutoConfiguration or DataSource or other injection dependencies even if the test doesn't require using data.
Can I suppress it somehow? I'm not any kind of Spring guru so my guess is that there is some simple configuration to handle this problem.
P.S. Ok, back on trees. Even with #DataJpaTest that test doesn't solve data dependencies. I tried add #AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) and it doesn't work either. I tried add #Transactional with the same result. It's a little bit beyond ridiculous.
If you aren't using JPA yet, then comment out / remove the dependency from build.gradle until you are using JPA. You need to define a datasource and other configuration details before the Hibernate and/or JPA configuration will complete successfully. Every application dependency gets resolved while #SpringApplicationConfiguration code is running, even if your current "hello world" test doesn't require JPA data.
My current unit tests actually have #SpringBootTest commented out. Here's a simplified view of how things are set up and working in my app's JPA related tests:
#RunWith(SpringJUnit4ClassRunner)
#SpringApplicationConfiguration(classes = DaemonApplication)
#ActiveProfiles('local')
//#SpringBootTest
#Transactional
public abstract class AbstractJpaTest extends AbstractTransactionalJUnit4SpringContextTests {
#BeforeTransaction
public void setupData() throws Exception {
deleteFromTables('User', 'User_Session', 'User_Handshake');
}
}
and then
class UserHandshakeRepositoryIntegrationTest extends AbstractJpaTest {
#Autowired UserHandshakeRepoImpl handshakeRepository;
#Test
public void testSave() {
UserHandshake handshake = handshakeRepository.save(new UserHandshake());
assertThat(handshake.getId(), is(notNullValue()));
}

Can not build an ApplicationContext with a NULL 'contextLoader'. Consider annotating your test class with #ContextConfiguration

I have a simple test class using junit. Unfortunatlly, it complains when i want to run the test case.
Here is the maven:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<version>1.4.3.RELEASE</version>
<scope>test</scope>
</dependency>
and then in the test class is:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = RestUploaderApplication.class)
public class RestUploaderApplicationTests {
Station station1;
Station station2;
Content content1;
Content content2;
ContentRepository contentRepo;
StationRepository stationRepo;
#Before
public void createObjects(){
Station station1=new Station("UT","Livii 2");
Station station2=new Station("City Center","Kissing Square");
Content content1=new Content(station1,"BMW","Text","google.com",10,true);
Content content2=new Content(station2,"SWB","Image","swb.com",100,true);
}
#Test
public void insertInstancesTest() {
int size=station1.getContents().size();
assertEquals(1,size);
}}
and finally, in the runtime the error appears:
Exception in thread "main" java.lang.NoSuchMethodError: org.junit.runner.notification.RunNotifier.testAborted(Lorg/junit/runner/Description;Ljava/lang/Throwable;)V
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at [org.springframework.test.context.support.DependencyInjectionTestExecutionListener#67b64c45] to prepare test instance a NULL 'contextLoader'. Consider annotating your test class with #ContextConfiguration.
at org.springframework.util.Assert.notNull(Assert.java:115)
I believe #SpringApplicationConfiguration was deprecated in spring boot 1.4.0.
I'm not sure but could try replacing #SpringApplicationConfiguration with #SpringBootTest eg
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public class RestUploaderApplicationTests {
SpringBootTest should automatically configure your spring context loader (while hopefully resloves the error: ...instance a NULL 'contextLoader'. Consider annotating your test class with #ContextConfiguration) and also automatically configures your MockMVC (although it appears you do not need in this case)
As an aside, if that is the actual test plan, it does't look like it needs to be Spring aware, there doesn't seem to be any spring components used, (#Beans / #MockBeans, #Autowired, #Controllers, etc) and regular old JUnit should be just fine
Hope this helps
EDIT // Added sources
#SpringApplicationConfiguration docs
#SpringBootTest docs

Unit testing with AspectJ in Java

I'm developing an application which uses AspectJ with Java. In development, I use ajc and java together. AspectJ calls some code segments when necessary and I want to test these code segments called by AspectJ. I tried to do it with Mockito but I failed, does anyone know any other way to test it?
I am not sure on how to do it in plain Java and JUnit, but if you have access to Spring-Integration-Test you can have an easy approach with the MockMVC and support classes that it offers.
And bellow you can see an example in which I am testing a controller that has an Aspect wrapped around it:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration
public class ControllerWithAspectTest {
#Autowired
private WebApplicationContext wac;
#Autowired
private MockMvc mockMvc;
#Autowired
#InjectMocks
private MongoController mongoController;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
// if you want to inject mocks into your controller
MockitoAnnotations.initMocks(this);
}
#Test
public void testControllerWithAspect() throws Exception {
MvcResult result = mockMvc
.perform(
MockMvcRequestBuilders.get("/my/get/url")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andExpect(MockMvcResultMatchers.status().isOk()).andReturn();
}
#Configuration
#EnableWebMvc
#EnableAspectJAutoProxy(proxyTargetClass = true)
static class Config extends WebMvcConfigurerAdapter {
#Bean
public MongoAuditingAspect getAuditingAspect() {
return new MongoAuditingAspect();
}
}
}
You can use the approach above even if you don't have Spring configured in your application, as the approach I've used will allow you to have a configuration class (can and should be a public class residing in it's own file).
And if the #Configuration class is annotated with #EnableAspectJAutoProxy(proxyTargetClass = true), Spring will know that it needs to enable aspects in your test/application.
If you need any extra clarification I will provide it with further edits.
EDIT:
The Maven Spring-Test dependency is:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
I just created a JUnit4 Runner to allow AspectJ Load Time Weaving on JUnit test cases. Here is a simple example:
I created a HelloService to return a greeting. And I created an Aspect to make names in the greeting upper case. Finally i created a unit test to use the HelloService with a lower case name and expect an upper case result.
All the details of the example are part of the GitHub project for reference:
https://github.com/david-888/aspectj-junit-runner
Just include the most up to date aspectj-junit-runner JAR in your classpath. Then your tests may look like this:
#AspectJConfig(classpathAdditions = "src/test/hello-resources")
#RunWith(AspectJUnit4Runner.class)
public class HelloTest {
#Test
public void getLiveGreeting() {
String expected = "Hello FRIEND!";
HelloService helloService = new HelloService();
String greeting = helloService.sayHello("friend");
Assert.assertEquals(expected, greeting);
}
}

Can I intercept Spring #Autowired process to do validation checks?

Sometimes we make mistakes in our code and #Autowired a prototype-scoped bean into a singleton-scoped bean. This is of course wrong because then the singleton is probably going to use that dependency as if it was also a singleton.
Is there any way of intercepting the autowiring/DI process to detect this and raise an error ? This would be for detection at development time.
The best way to achieve this is through your unit tests. For example:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = MyAppConfig.class, loader = AnnotationConfigContextLoader.class)
public class MyServiceTest {
#Autowired(required = true)
MyService myService;
#Test
public void shouldAutowire() {}
}
The #ContextConfiguration can be used with Java config as above, or it can refer to XML config files. By doing this, Spring will be used to inject all of your dependencies whenever you run your tests. By including "required = true" on your #Autowired beans, you are ensuring that Spring will throw an exception during that phase, and your test will fail. The example above may not look fancy, but it will ensure that any configuration errors are caught. Of course, you can go further and have your tests make use of the injected beans. I find that rather handy for database access integration tests.
This is not intercepting the autowiring process itself, but you can of course test that your beans are behaving correctly.
You will need to import the spring-test dependency. i.e. For Maven:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>

Categories