Test spring boot app with servlet parameters in application.yml - spring

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.

Related

Unable to inject(autowire) a service when test elasticsearch with testcontainers

So, I want to test elasticsearch with testcontainers. Test containers work well and start good, but when I want to invoke a method from my service this is null, because is not injected, and I don't why.
#Testcontainers
#SpringBootTest
#ActiveProfiles("dev")
public class ArticleServiceTestDemo {
#Autowired
private ArticleService articleService;
private static final String ELASTICSEARCH_VERSION = "7.9.2";
private static final DockerImageName ELASTICSEARCH_IMAGE = DockerImageName
.parse("docker.elastic.co/elasticsearch/elasticsearch")
.withTag(ELASTICSEARCH_VERSION);
#Test
public void myTest() {
try (
ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE);
) {
container.start();
ResponseEntity<Article> article = articleService.persistArticle(new ArticleRequestDto());
assertTrue(article.getStatusCode().is2xxSuccessful());
}
}
}
This is my error:
java.lang.NullPointerException: Cannot invoke "com.ArticleService.persistArticle(com.ArticleRequestDto)" because "this.articleService" is null
When I run a simpe SpringBootTest (without elasticsearch connection and testcontainers) the ArticleService was injected good. But in test with testcontainers this not injected.
Please check ArticleService class is spring bean or not, means ArticleService class was annoted #Service or #Component annotaion or not.
When you apply #Component on a class, then Spring bean container initialize as bean object in Spring Bean Container and Spring Container is trying to inject initialized bean object to annoted #Autowired field. And if you apply #Component on class NullPointerException occurs.
For example this test runs good. And ArticleService was injected.
#SpringBootTest
public class DemoTest {
#Autowired
private ArticleService articleService;
#Test
public void simpleTest() {
articleService.persistArticle(new ArticleRequestDto());
}
}
Can you check if #ActiveProfiles("dev") isn't messing up the beans wiring?
Your example that runs okay doesn't have it. For example remove it and verify if the test runs?
On another note, if you're managing the lifecycle of the testcontainers yourself by calling: container.start(); then you don't need the #Testcontainers annotation which is from JUnit extension for Testcontainers that looks at the static/instance fields marked with #Container and creates & starts/stops these automatically.

spring boot: how to configure autowired WebTestClient

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.

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;

Spring HATEOAS Resource assembler is not instantiated in unit test

I am trying to write a unit test for a REST controller which generates HATEOAS links via Resource assembler class. Everything is OK in production, but with the unit test Resource assembler class is not being injected into the controller.
my resource assembler class is:
#Component
public class ModelResourceAssembler extends ResourceAssemblerSupport<Model, ModelResource> {
public ModelResourceAssembler() {
super(ModelRestController.class, ModelResource.class);
}
#Bean
public ModelResourceAssembler modelResourceAssembler(){
return new ModelResourceAssembler();
}
#Override
public ModelResource toResource(Model model) {
...
}
}
The controller is:
#Controller
#RequestMapping("/demo")
#ComponentScan(basePackages = {"com.foo.demo"} )
public class ModelRestController {
#Autowired
private ModelPersistenceHandler modelPersistenceHandler;
#Autowired
private ModelResourceAssembler modelResourceAssembler;
...
}
And the unit test:
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration(loader = AnnotationConfigContextLoader.class, classes= {ModelResourceAssembler.class, ModelRestController.class})
public class ModelRestControllerTest {
private MockMvc mockMvc;
#InjectMocks
private ModelRestController modelRestController;
#Mock
private ModelPersistenceHandler modelPersistenceHandler;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(modelRestController).build();
}
...
}
No matter what I do the ModelResourceAssembler instance is always null. Since the application is Spring Boot it does not have the WebCoonfig classes and autowired WebApplicationContext is always null, so I cannot (and really don't want to since I am running a unit test) instantiate MockMvc via webAppContextSetup
The solution ended up being quite simple: I needed to add one line to my test:
#Spy
private ModelResourceAssembler modelResourceAssembler;
And the bean was instantiated and properly wired
In your example you use #InjectMocks but don't declare a mock for ModelResourceAssembler. You don't get an instance out of nowhere.
You use the MockitoJUnitRunner.class. It has no idea of Spring beans. For testing Spring applications you rather want to use SpringJUnit4ClassRunner.class.
If i may suggest, if you use constructor injection for your controller then you can just mock the dependency and not need spring junit test runner stuff.

Resources