spring boot: how to configure autowired WebTestClient - spring

Are there any properties available in Spring Boot to configure the #Autowired WebTestClient? For instance, how to set the servlet context path (or just some base path for that matter) on WebTestClient?
Here's how my web tests are configured now:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
#ActiveProfiles("test")
public class MyTestClass{
#Autowired
private WebTestClient cl;
//the rest of it
}
In other words, what is Spring Boot equivalent of
WebTestClient client = WebTestClient.bindToServer()
.baseUrl("http://localhost:<random port>/myServletContext").build();
I didn't find anything useful in documentation:
https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html

building webTestClient using current application context, no need to hardcode uri and port number
#Autowired
ApplicationContext context;
#Autowired
WebTestClient webTestClient;
#Before
public void setup() throws Exception {
this.webTestClient = WebTestClient.bindToApplicationContext(this.context).build();
}

You can use something like server.servlet.context-path=/myServletContextPath.

Related

Test spring boot app with servlet parameters in application.yml

In application.yml there is the following parameter:
server:
context-parameters:
appCode: MYAPPCODE
This parameter is read by a third-party library. When running on embedded Tomcat, the parameter is available in the ServletContext. However, when running a test on SpringRunner, the ServletContext has no parameter.
Here is the relevant part of the test class.
#RunWith(SpringRunner.class)
#SpringBootTest
#WebAppConfiguration
#AutoConfigureMockMvc
public class RestControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
private void create() {
String content = createContent();
mockMvc.perform(post("/api/entity/create").content(content))
.andExpect(jsonPath("id").isNumber());
}
}
How can I configure the test class so that the mocked ServletContext is set with the context parameters from the application.yml?
Currently, to overcome the problem, I did the following workaround in the test class.
#Autowired
private ServletContext servletContext;
#Autowired
private ServerProperties serverProperties;
#Before
public void setup() throws Exception {
Map<String, String> params = serverProperties.getContextParameters();
new InitParameterConfiguringServletContextInitializer(params)
.onStartup(servletContext);
}
How can I configure the test class so that the mocked ServletContext
is set with the context parameters from the application.yml?
AFAIK, that is currently not possible when using WebEnvironment.MOCK (i.e., the default mode for #SpringBootTest) since the MockServletContext used by the WebApplicationContext configured by Spring Boot is plain vanilla (i.e., not populated with external values).
Thus, your "workaround" is the only solution.

Spring Web Service Test with MongoDB

I'm writing service tests for my Spring Boot Web application that acts as an interface to MongoDB. Ideally, my service test will test each component of my Spring application before finally hitting a Mocked MongoTemplate. The following code uses a MockMvc to hit my web endpoints.
#RunWith(SpringRunner.class)
#WebMvcTest(MyController.class)
#AutoConfigureDataMongo
public class MyControllerServiceTest {
#Autowired
private MockMvc mvc;
#Autowired
private MongoTemplate mongoTemplate
#SpyBean
private MyMongoRepository myMongoRepository;
#Test
public void createTest() {
MyObject create = new MyObject()
given(this.myMongoRepository.insert(create));
this.mvc.perform(post("localhost:8080/myService")...)...;
}
}
MyController contains an #Autowired MyMongoRepository, which in turn implements MongoRepository and necessitates a mongoTemplate bean. This code executes properly only if a running MongoDB instance can be found (this example is more of an integration test between my service and MongoDB).
How can I mock out MongoTemplate while still using my MockMvc?
You need to add the following line to your test unit:
#MockBean
private MongoTemplate mongoTemplate;
For example, your class should look like this:
#RunWith(SpringRunner.class)
#WebMvcTest(MyController.class, excludeAutoConfiguration = EmbeddedMongoAutoConfiguration.class)
public class MyMvcTests {
#Autowired
private MockMvc mvc;
#MockBean
private MyRepository repository;
#MockBean
private MongoTemplate mongoTemplate;
#Test
public void someTest() {}
}
You can find a complete Spring Boot application that include Integration and Unit tests here.
I think a better approach to test would be to test your web layer(controller) and your service layer separately.
For testing your web layer you can use MockMvc and you can mock your service layer.
For testing your service layer which in turn talks to mongo, you can use a Fongo and nosqlunit.
Some examples here
https://arthurportas.wordpress.com/2017/01/21/sample-project-using-spring-boot-and-mongodbfongo-and-test-repository-with-nosqlunit/
https://github.com/JohnathanMarkSmith/spring-fongo-demo

Cannot inject #Service in Unit Test in SpringBoot project

i have a #Service that I am trying to mock in an Unit Test but i get a null value so far. In the application class I specify what are the scanBasePackages. Do I have to do this in a different way? Thanks.
This is my service class that implements an interface:
#Service
public class DeviceService implements DeviceServiceDao {
private List<Device> devices;
#Override
public List<Device> getDevices(long homeId) {
return devices;
}
}
This is my unit test.
public class SmartHomeControllerTest {
private RestTemplate restTemplate = new RestTemplate();
private static final String BASE_URL = “..”;
#Mock
private DeviceService deviceService;
#Test
public void getHomeRegisteredDevices() throws Exception {
Device activeDevice = new DeviceBuilder()
.getActiveDevice(true)
.getName("Alexa")
.getDeviceId(1)
.getHomeId(1)
.build();
Device inativeDevice = new DeviceBuilder()
.getInactiveDevice(false)
.getName("Heater")
.getDeviceId(2)
.getHomeId(1)
.build();
UriComponentsBuilder builder = UriComponentsBuilder
.fromUriString(BASE_URL + "/1/devices");
List response = restTemplate.getForObject(builder.toUriString(), List.class);
verify(deviceService, times(1)).getDevices(1);
verifyNoMoreInteractions(deviceService);
}
You have to use a Spring test runner if you want to load and use a Spring context during tests execution.
You don't specify any runner, so it uses by default the runner of your test API. Here is probably JUnit or TestNG (the runner using depends on the #Test annotation specified).
Besides, according to the logic of your test, you want to invoke the "real"
REST service :
List response = restTemplate.getForObject(builder.toUriString(),
List.class);
To achieve it, you should load the Spring context and load the Spring Boot container by annotating the test with #SpringBootTest.
If you use a Spring Boot context, to mock the dependency in the Spring context, you must not use #Mock from Mockito but #MockBean from Spring Boot.
To understand the difference between the two, you may refer to this question.
Note that if you are using the #SpringBootTest annotation, a TestRestTemplate is automatically available and can be autowired into your test.
But beware, this is fault tolerant. It may be suitable or not according to your tests.
So your code could look like :
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class SmartHomeControllerTest {
private static final String BASE_URL = “..”;
#Autowired
private TestRestTemplate restTemplate;
#MockBean
private DeviceService deviceService;
#Test
public void getHomeRegisteredDevices() throws Exception {
...
}
As a side note, avoid using raw type as List but favor generic type.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = NotificationApplication.class)
public class EmailClientImplTest {
...
}
And also add the needed properties/configs in
/src/test/resources/application.yml
Good luck!
I figured it out, I am using Mockito and used that to annotate my test class. This allowed me to get a mock of the service class that i am trying to use.
#RunWith(MockitoJUnitRunner.class)
public class SmartHomeControllerTest {..
#Mock
private DeviceService deviceService;
}
Try with #InjectMock instead of #Mock
You should run your test with spring boot runner

Null Service while running JUnit test with Spring

I am developing a JUnit test into a Spring Controller, the Controller calls a bunch of Services. My JUnit test code is above:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {WebApplicationConfiguration.class, DatabaseConfiguration.class, ServiceConfiguration.class})
#WebAppConfiguration
public class ProcessFileControllerTest {
private MockMvc mockMvc;
#Test
public void getPageTest() throws Exception{
final ProcessFileController controller = new ProcessFileController();
SecurityHelperUtility.setupSecurityContext("user", "pass", "ROLE_ADMIN");
mockMvc = standaloneSetup(controller).build();
mockMvc.perform(get(URI.create("/processFile.html")).sessionAttr("freeTrialEmailAddress", "")).andExpect(view().name("processFile"));
}
When I run the JUnit test, I get a NullPointerExcelption when I call this particular line of code of my ProcessFileController.java
final Company company = companyService.getCompanyByUser(userName);
I can see that the Serviceis Null.
In my ProcessFileController.java the Service is being declared as:
#Autowired
private CompanyService companyService;
Any clue? Thanks in advance.
You are creating your controller using the new keyword. It should be a spring managed bean for spring to inject its dependencies. Use #Autowired
#Autowired
ProcessFileController controller;

#SpringApplicationConfiguration without #IntegrationTest

right now, I start my Spring Boot Integration tests with
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = {Application.class})
#WebAppConfiguration
#org.springframework.boot.test.IntegrationTest
#ActiveProfiles("it")
public class AbstractIntegrationTest {
protected MockMvc mockMvc;
#Inject
private EmbeddedWebApplicationContext webApplicationContext;
#Inject
private RequestHeaderAuthenticationFilter requestHeaderAuthenticationFilter;
#Before
public void mockMvcSetUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.addFilter(requestHeaderAuthenticationFilter)
.build();
}
}
However, this always starts a fully leaded servlet container that listens on a port because my Application.class is annotated with #EnableAutoConfiguration.
Since I use MockMvc instead of RestTemplates (I like the fluent interface of MockMvc way more than RestTemplates), I figure it should be possible to not start a fully loaded container.
Could anyone please educate me if this is possible and if so, how?
Thanks,
Robin

Resources