What is the use of contextLoads method in Spring Boot Junit Testcases? - java

This method is empty in all my JUnit test cases. What is the use of this method?
Sonarqube is complaining
"Add a nested comment explaining why this method is empty, throw an UnsupportedOperationException or complete the implementation."
I can bypass this by adding some comment but I just want to know why it is necessary.

When you build a Spring boot application using Spring Initializer. It auto creates a test class for you with contextLoads empty method.
#SpringBootTest
class ApplicationContextTest {
#Test
void contextLoads() {
}
}
Note the use of #SpringBootTest annotation which tells Spring Boot to look for a main configuration class (one with #SpringBootApplication, for instance) and use that to start a Spring application context. Empty contextLoads() is a test to verify if the application is able to load Spring context successfully or not.
If sonarqube is complaining about the empty method then you can do something like this to verify your controller or service bean context:-
#SpringBootTest
public class ApplicationContextTest {
#Autowired
private MyController myController;
#Autowired
private MyService myService;
#Test
public void contextLoads() throws Exception {
assertThat(myController).isNotNull();
assertThat(myService).isNotNull();
}
}

Use different runner,
if you are using SpringRunner.class, use an alternative one such as MockitoJUnitRunner.class or MockitoJunitRunner.class rather then SpringRunner.class
#Runwith(SpringRunner.class)
#Runwith(JUnit4ClassRunner.class)
#Runwith(MockitoJUnit4Runner.class)
#Runwith(MockitoJUnitRunner.class)

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

ServletContext cannot open properties file when executing JUnit test

I'm testing a REST controller using JUnit 4 and MockMvc. When I've written the test a few weeks ago, everything worked as expected. I've done some modifications in my code but I didn't change the JUnit test. Now, when I'm trying to run my tests, I have the error:
Caused by: java.io.FileNotFoundException: Could not open ServletContext resource [/application.properties]
Here is my code:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = MyServerApplication.class)
#SpringBootTest
#Transactional
public class MovieControllerTest {
private MockMvc mockMvc;
#Autowired
private MovieRepository movieRepository;
#Autowired
private WebApplicationContext wac;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
// Some tests
}
And my main class:
#SpringBootApplication
public class MyServerApplication{
public static void main(String[] args) {
SpringApplication.run(MyServerApplication.class, args);
}
}
My application.properties file is located in src/main/resources. I didn't move this file, I didn't do anything but add some code in my services and add some properties in my file.
I read SO questions & doc, and tried these solutions:
Check that src/main/resources is still in my test classpath
Add #PropertySource("classpath:application.properties") under the annotations in my test ; it didn't work so I tried to create a src/test/resources with a copy of application.properties inside, as suggested in one post
Add #PropertySource("classpath:application.properties") in the main class instead of the test class
Add #WebAppConfiguration annotation
Add #WebMvcTest annotation
I didn't try all of these solutions at the same time of course, I removed the added code after each failure.
I can still run my code without any issue though, only the test class results in FileNotFoundException.
How to solve this? And why do I have an issue with the test class but everything working fine when I run my server?
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = MyServerApplication.class)
#SpringBootTest
#Transactional
public class MovieControllerTest { ... }
This is what you have on your test class. When using #SpringBootTest you shouldn't be using #ContextConfiguration (see testing chapter of the Spring Boot Reference Guide).
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
#Transactional
public class MovieControllerTest { ... }
I would also suggest you use Spring Boot for testing instead of trying to do things manually. For mock mvc testing Spring Boot applications there are special slices and setup already done for you.
To enable this add #AutoConfigureMockMvc to your test and put #Autowired on the MockMvc field (and remove the setup in your #Before method).

Remove exception with bean creation on integration test, which does not appear on standard application start

I have spring boot application which works well, but when I started to work on integration tests, I discovered, that there is cyclic dependency in project:
#Service
public class CrowdManagerSyncService {
private final CrowdManagerSyncScheduler crowdManagerSyncScheduler;
#Autowired
public CrowdManagerSyncService(CrowdManagerSyncScheduler crowdManagerSyncScheduler) {
this.crowdManagerSyncScheduler = Objects.requireNonNull(crowdManagerSyncScheduler);
}
}
And
#Component
public class CrowdManagerSyncScheduler {
#Autowired
private CrowdManagerSyncService crowdManagerSyncService;
}
It is not my code and I am not ready to rewrite it right now. But it works perfectly well in production. In my integration test
#RunWith(SpringRunner.class)
#WebMvcTest(UserController.class)
#WithMockUser(roles={"ADMIN"})
#ContextConfiguration(classes = {AdminConsoleApplication.class, DataSourceAutoConfiguration.class,
MockMvcAutoConfiguration.class, MockMvcWebDriverAutoConfiguration.class})
public class UserControllerTest {
#Autowired
private MockMvcHtmlUnitDriverBuilder builder;
private WebDriver webDriver;
#Before
public void setUp() throws Exception {
webDriver = builder.build();
}
}
I catch exception:
Error creating bean with name 'crowdManagerSyncService': Requested bean is currently in creation: Is there an unresolvable circular reference?
So, my question is: how to omit this problem in testing without removing that awful circular dependency? It works well in production, so pretty sure there is some way to start test context without code change.
#WebMvcTest is not suitable for "proper" integration tests.
From the api docs:
Can be used when a test focuses only on Spring MVC components.
However, you're then using #ContextConfiguration to essentially add your whole application to the test.
Remove the #ContextConfiguration and instead autowire a #MockBean CrowdManagerSyncService into your test.
This creates a mock version of CrowdManagerSyncService and injects it into the UserController in the test application context.
#RunWith(SpringRunner.class)
#WebMvcTest(UserController.class)
#WithMockUser(roles={"ADMIN"})
public class UserControllerTest {
#Autowired
private MockMvcHtmlUnitDriverBuilder builder;
#MockBean
private CrowdManagerSyncService service;
private WebDriver webDriver;
#Before
public void setUp() throws Exception {
webDriver = builder.build();
}
#Test
public void shouldWork() {
when(service.doStuff())
.thenReturn("Hello"); // regular Mockito mocking
}
}
This is appropriate if you're just trying to test the UserController and sidesteps the circular dependency problem because there's no instantiation of a "real" CrowdManagerSyncService anywhere.
You can also replace #WebMvcTest and #ContextConfiguration with both #SpringBootTest (which bootstraps the application just like production) and #AutoConfigureMockMvc (which replaces the real HTTP stuff with MockMvc).

Injecting Mockito Mock objects using Spring JavaConfig and #Autowired

I'm trying to replace an #Autowired object with a Mockito mock object. The usual way of doing this was with xml using Springockito:
<mockito:mock id="SomeMock" class="com.package.MockInterface" />
Currently I'm trying to move over to using Spring's JavaConfig to do the job. All of a sudden the Java expressions are a whole lot more verbose than xml:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class MyTestClass {
#Configuration
static class Config {
#Bean
public MockInterface somethingSpecial() {
return Mockito.mock(MockInterface.class);
}
}
#Autowired MockInterface mockObj;
// test code
}
I discovered a library called Springockito-annotations, which allows you to do the following:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader=SpringockitoContextLoader.class)
public class MyTestClass {
#Autowired #ReplaceWithMock MockInterface mockObj;
// test code
}
Clearly, a whole lot prettier :) The only problem is that this context loader doesn't allow me to use #Configuration and JavaConfig for other beans (if I do, Spring complains that there are no candidates that match those autowired fields).
Do you guys know of a way to get Spring's JavaConfig and Springockito-annotations to play nice? Alternatively, is there another shorthand for creating mocks?
As a nice bonus, using Springockito and xml config, I was able to mock out concrete classes without providing autowiring candidates to its dependencies (if it had any). Is this not possible without xml?
Moving away from the now unmaintained (as of this writing) Spingockito-annotations and to Mockito, we have a way of doing this very simply:
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration
public class MyTestClass {
#Mock MockInterface mockObj;
// test code
}
If you're using a real object, but would like to mock a dependency within it, for instance testing a service layer with DAO:
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration
public class MyTestClass {
#InjectMocks RealService;
#Mock MockDAO mockDAO;
// test code
}
Finally, this can also be applied to Spring-boot, but using annotation initialization within setUp() until multiple class runners are supported:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = MyMainSpringBootClass.class)
public class MyTestClass {
#InjectMocks RealService;
#Mock MockDAO mockDAO;
#Before
public final void setUp() throws Exception{
MockitoAnnotations.initMocks(this);
}
// test code
}
Outdated and deprecated!
Read about mocking and spying in Spring Boot 1.4
Please read also #ethesx answer,
Springockito is unmaintaned
Old answer
This is possible now to mock Spring application without any XML file with Springockito-annotations.. This solution works also with Spring Boot.
import static org.mockito.BDDMockito.*;
import org.kubek2k.springockito.annotations.*;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = Application.class,
loader = SpringockitoAnnotatedContextLoader.class)
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class MainControllerTest {
#Autowired
MainController mainController;
#Autowired
#ReplaceWithMock
FooService fooService;
#Test
public void shouldGetBar() {
//given
given(fooService.result("foo")).willReturn("bar");
//when
Bar bar build = fooService.getBar("foo");
//then
assertThat(bar).isNotNull();
}
}
Dependencies: org.kubek2k:springockito-annotations:1.0.9
It appears that SpringockitoContextLoader extends GenericXmlContextLoader which is described as:
Concrete implementation of AbstractGenericContextLoader that reads bean definitions from XML resources.
So you are limited to xml bean definitions at the moment.
You could write your own context loader, taking relevant parts from the SpringockitoContextLoader class. Take a look here to get started, perhaps you could extend AnnotationConfigContextLoader for example?

integration testing spring service layer based on migrated data

#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"/applicationContext-test.xml"})
#Transactional
public class MyServiceTest {
#Resource(name="myService")
public MyService myService;
#Test
public void testSeomthing() {
//do some asserts using myService.whatever()
}
}
However the tests are based on data I migrate in, so every time I run my suite of tests I want to execute my unrelated migration code. I don't want to run a #Before in each test class. I want to run it once at beginning of complete test process, where can I put this ?
I would advice you to create a test bean somewhere with startup logic invoked in #PostConstruct:
#Service
public class TestBean {
#PostConstruct
public void init() {
//startup logic here
}
}
Obviously this bean should only be created for tests, the easiest way to achieve this is to place it in src/test/java in a package that is component-scanned by Spring for #Service-annotated classes.
Note: you must remember that #PostConstruct is not running in a transaction! See How to call method on spring proxy once initialised.
JUnit also offers a #BeforeClass annotation which you can place on a static method to initialize resources just once.

Categories