Autowiring a field in a Cucumber step produces NullPointerException - java

Cucumber runner configuration :
#RunWith(Cucumber.class)
#CucumberOptions(features = "/features",glue {""})
#ContextConfiguration(classes = Init.class,
initializers = ConfigFileApplicationContextInitializer.class)
public class Test {
}
Feature file :
class feature {
Scenario: Save test in db.
When I inject a payload in the aws queue
Then it should be integrated in database
}
Step Definition file::
#CucumberAbstract
#ContextConfiguration("classpath:application.yml")
public class MyStepdefs {
#Autowired
private QueueMessagingTemplate queueMessagingTemplate;
#Autowired
TestRepository testRepository;
String message = "Hi";
public MyStepdefs() {
When("^I inject a payload in the aws queue$", () -> {
this.queueMessagingTemplate.convertAndSend("QUEUE_NAME", message);
});
}
#Then("^it should be integrated in database$")
public void itShouldBeIntegratedInDatabase() throws Throwable {
//validations
}
}
I tried creating a custom annotation here and using this annotation on top of stepDefinition file.
AbstractTest :::
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#ContextConfiguration(classes = Init.class, loader =
SpringApplicationContextLoader.class)
#SpringBootTest
#RunWith(SpringJUnit4ClassRunner.class)
public #interface CucumberAbstract {
}

The Cucumber runner (cucumber.api.junit.Cucumber) doesn't load any Spring context.
So your cucumber step class is not Spring aware.
To inject a bean into a field of your class, you have to load the Spring context, in your case the Spring Boot context.
So specifying #SpringBootTest is what you are looking for :
#SpringBootTest
public class MyStepdefs {
...
}

Actually what you need is to integrate Cucumber with Spring Boot.
So you need to add these dependencies for you maven project:
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>4.7.4</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit</artifactId>
<version>4.7.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-spring</artifactId>
<version>4.7.4</version>
</dependency>
Your cucumber runner is the same:
#RunWith(Cucumber.class)
#CucumberOptions(plugin = {"pretty"}, features = {"src/main/resources"})
public class ReportCaseCucumberTest {
}
Now in you StepDefinition class you need to configure the ContextConfiguration to be able to use Spring.
#SpringBootTest
#ContextConfiguration(
classes = Application.class,
loader = SpringBootContextLoader.class)
public class StepsDefinitions {
#Autowired
private ApplicationContext appContext;
}
Now in you StepDefinition class you can use "Autowired" annotation to inject beans.
This solved my problem, also you can check this tutorial:
https://medium.com/#bcarunmail/set-up-and-run-cucumber-tests-in-spring-boot-application-d0c149d26220

Related

SpringJUnit4ClassRunner does not load the test properties file

I am trying to boot up a spring boot test, with a mapstruct implementation provided.
I am using the configuration below, but the application.properties file in the resources directory is not being read into the test.
How do I get the test below to read the default properties file, the file exist in test/resources and other spring boot tests are able to load it.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SpringBootExampleConfig.class)
public class SpringBootExampleTest {
#Autowired
private IMapper mapper;
#Test
public void test() {
}
#Configuration
#ComponentScan(basePackageClasses = SpringBootExampleTest.class)
public static class SpringTestConfig {
}
}

Null pointer Exception when injecting a rest-assured #Steps into spring-boot Test

Am new to springboot and am trying to integrate Integration Tests using Rest-assured to test my Rest-Api.
Am getting NPE when injecting #Steps into SpringBoot Test.I'm introducing a step class to improve on re-usability code.This test runs well if the step method is in the IT-class.I tried #Component annotation but it didn't work
Step class
import net.thucydides.core.annotations.Step;
import org.apache.http.HttpStatus;
import static com.jayway.restassured.RestAssured.when;
public class StaffSteps {
protected static String BASE_STAFF_URL = "/api/v1a/staff/";
protected static Staff staff;
#Step
public StaffSteps getStaffMemberById(String id){
staff = when().get(BASE_STAFF_URL+id)
.then().assertThat()
.statusCode(HttpStatus.SC_OK)
.extract()
.as(Staff.class);
return this;
}
#Step
public Staff getStaff(){return staff;}
}
import net.thucydides.core.annotations.Steps;
import org.apache.http.HttpStatus;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.Arrays;
import java.util.List;
import static com.jayway.restassured.RestAssured.when;
#RunWith(SpringJUnit4ClassRunner.class)
public class StaffControllerIT extends BaseTest {
#Steps
private StaffSteps staffSteps;
#Before
public void setUp(){
}
#Test
public void getStaffMemberById(){
String id ="ff8081817049a34e017049a379320000";
Staff staff = staffSteps.getStaffMemberById(id).getStaff();
System.err.println(staff);
}
When i run this test, staffSteps is null.
Here is my dependency i used
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-core</artifactId>
<version>1.9.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>3.0.0</version>
<scope>test</scope>
</dependency>
Please let me know if you need more information on this. Thanks
Solution using SpringRunner:
Annotate the steps with #Bean and #StepScope, and as a result, this object will share its lifetime with StepExecution.
public class StaffStepsConfig {
protected static String BASE_STAFF_URL = "/api/v1a/staff/";
protected static Staff staff;
#Bean
#StepScope
public StaffSteps getStaffMemberById(String id){
staff = when().get(BASE_STAFF_URL+id)
.then().assertThat()
.statusCode(HttpStatus.SC_OK)
.extract()
.as(Staff.class);
return this;
}
#Bean
#StepScope
public Staff getStaff(){return staff;}
}
In the Test class, the spring-batch-test dependency provides a set of useful helper methods and listeners that can be used to configure the Spring Batch context during testing.
#RunWith(SpringRunner.class)
//#SpringBatchTest
//#SpringBootTest
#EnableAutoConfiguration
#ContextConfiguration(classes = { StaffStepsConfig.class })
#DirtiesContext(classMode = ClassMode.AFTER_CLASS)
public class StaffControllerIT extends BaseTest {
#Autowired
private StaffSteps staffSteps;
#Before
public void setUp(){
}
#Test
public void getStaffMemberById(){
String id ="ff8081817049a34e017049a379320000";
Staff staff = staffSteps.getStaffMemberById(id).getStaff();
System.err.println(staff);
}
}
Note: This runner recognizes #SpringBootTest . I think the problem is in the way the test outcomes are generated. The steps are not being read by Serenity. Spring will inject #Autowired classes, and #serenity will inject #steps classes. I assume this happens because serenity and spring are creating components in different contexts .
required dependencies in your pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
<version>2.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.1.9.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-test</artifactId>
<version>4.2.0.RELEASE</version>
<scope>test</scope>
</dependency>

Spring context can not be loaded in Spock test method

Hi I have a Spring Boot (2.1.6 RELEASE) application, and I am trying add some simple integration test to my app.
Firstly I've create a base IntegrationTest class like below:
#TypeChecked
#Transactional
#Rollback
#SpringBootTest(classes = AppRunner.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
abstract class IntegrationTest extends Specification {
#Autowired
protected WebApplicationContext webApplicationContext
#Autowired
ObjectMapper objectMapper
MockMvc mockMvc
#Before
void setupMockMvc() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.apply(springSecurity())
.build()
}
protected ResultActions makePost(final String uri, final Object dto) {
mockMvc.perform(post(uri)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(dtoToJson(dto))
)
}
private String dtoToJson(final Object dto) {
return objectMapper.writeValueAsString(dto)
}
}
This is my test method:
class TestForTest extends IntegrationTest {
#WithMockUser(username = "xxx#gmail.com")
def "should reset password and send mail with proper activation link"() {
given:
def email = "user2#xxx.com"
when:
ResultActions result = makePost("/rest/user/resendActivationMail", email)
then:
1 == 1
println(result)
}
}
Is strange because I have feeling the context was not be loaded, and so more there was no any try to do this.
I got error:
java.lang.IllegalArgumentException: WebApplicationContext is required
at org.springframework.util.Assert.notNull(Assert.java:198)
at org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder.<init>(DefaultMockMvcBuilder.java:52)
at org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup(MockMvcBuilders.java:51)
at pl.isolution.veolia.mveolia.spec.IntegrationTest.setupMockMvc(IntegrationTest.groovy:37)
Is there is a problem with Maven or IntellijIDEA? This took abot 340 ms, and I didn't see any attempt of context loading.
My stack:
JAVA 11, Maven 3, Spring Boot 2.1.6, Spock 1.3-groovy-2.5
Any suggestions?
Ok What a shame :) I missed a dependency.
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>1.3-groovy-2.5</version>
<scope>test</scope>
</dependency>

junit spring boot profile properties not being read

I am trring to read a value from the properties file in my junit setup using spring boot.
I can not read the value. Below is my content:-
application-test.properties
my.user.name=Amar
COnfig file to create beans:
#Configuration
#ActiveProfiles("test")
#Profile("test")
public class RdbmsTestConfig {
#Value("${my.user.name}")
private String name;
#Bean
public String myString(){
return "Amar";
}
#Bean
public PropsHolder propsHolder() {
PropsHolder propsHolder = new PropsHolder();
propsHolder.setUserName(name);
return propsHolder;
}
}
My test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = RdbmsTestConfig.class)
#ActiveProfiles("test")
public class TestRoomService {
#Autowired
#Qualifier("myString")
private String myString;
#Autowired
private PropsHolder propsHolder;
#Autowired
private Environment env;
#Test
public void userTest() {
Arrays.stream(env.getActiveProfiles()).forEach(System.out::println);
System.out.println(propsHolder.getUserName());
Assert.assertNotNull(myString);
Assert.assertEquals("Amar",myString);
}
}
The value for propsHolder.getUserName comes out to be ${my.user.name}
First remove #ActiveProfiles("test") from your class RdbmsTestConfig. Then your test just defines the RdbmsTestConfig as spring context. As I can see you do not run a real spring boot test. The problem is you do not have any PropertyPlaceholderConfigurer configured in your spring config. So either configure one PropertyPlaceholderConfigurer or add #SpringBootTest to your test if you have any SpringBootApplication.
I've never used #Profile(), so i'm not sure if that is supposed to do what you want it to do. But I'm always using #PropertySources(), because otherwise, how is the code supposed to know where to look for the properties?
#Configuration
#PropertySources(value = { #PropertySource("classpath:core-
test.properties") })
I created a base test class that has the required annotations:-
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = RdbmsTestConfig.class)
#ActiveProfiles("test")
#SpringBootTest
public abstract class BastTest { }
#ActiveProfiles set the profile to use used, I dont have to mention it in the application.properties file
My test class now extends this:-
public class TestRoomService extends BastTest
In my RdbmsTestConfig remove #ActiveProfiles annotation.

How do I unit test Spring Controller using annotations?

I am new to the concept of unit testing with Spring controllers. I'm following some examples I found online and trying to implement their testing strategy. This is my basic controller:
#Controller
public class GreetingController {
#RequestMapping("/greeting")
public String greeting(#RequestParam(value = "name2", required = false, defaultValue = "World2") String name2,
Model model) {
model.addAttribute("name", name2);
return "greeting";
}
}
This is my unit test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
#WebAppConfiguration
public class ControllerGreetingTest {
private MockMvc mockMvc;
#Autowired
GreetingController greetingController;
#Test
public void shouldReturnSomething() throws Exception {
mockMvc.perform(get("/greeting"))
.andExpect(status().isOk())
.andExpect(view().name("greeting"));
}
}
Seems pretty straight forward but I get the following error:
java.lang.IllegalStateException: Neither GenericXmlWebContextLoader
nor AnnotationConfigWebContextLoader was able to detect defaults, and
no ApplicationContextInitializers were declared for context
configuration [ContextConfigurationAttributes#1698539 declaringClass =
'com.practice.demo.ControllerGreetingTest', locations = '{}', classes
= '{}', inheritLocations = true, initializers = '{}', inheritInitializers = true, name = [null], contextLoaderClass =
'org.springframework.test.context.ContextLoader']
I'm assuming I have to add a parameter to the #ContextConfiguration annotation but not sure what to include in there.
EDIT = This is what I have so far:
public class ControllerGreetingTest {
private MockMvc mockMvc;
#Before
public void setup(){
this.mockMvc = standaloneSetup(new GreetingController()).build();
}
#Test
public void shouldReturnDefaultString() throws Exception {
mockMvc.perform(get("/greeting"))
.andExpect(status().isOk())
.andExpect(view().name("greetings"))
.andExpect(model().attribute("name","World2"));
}
}
It does the job but it doesn't use any of the Spring annotations like I tried to do before.. this approach is not good so trying to figure out why I keep gettings errors whenever I include the annotations in my test file.
My POM:
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>1.5.7.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>hamcrest-core</artifactId>
<groupId>org.hamcrest</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.2.3.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mongodb/mongo-java-driver -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>1.5.7.RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>
You should use spring-boot-starter-test dependency. It has almost everything for testing.
And for testing controller part of a spring application, you should use #WebMvcTest annotation for your test class. With this annotation spring will load context just for controller part. Plus you don't need to setup method if you use this annotation. You can simply autowire mockMvc. Your test class should be like this:
#RunWith(SpringRunner.class)
#WebMvcTest(GreetingController.class)
public class ControllerGreetingTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private SomeServiceClass someServiceClass;
#Test
public void shouldReturnDefaultString() throws Exception {
mockMvc.perform(get("/greeting"))
.andExpect(status().isOk())
.andExpect(view().name("greetings"))
.andExpect(model().attribute("name","World2"));
}
}
Note: Your controller does not have any autowired fields. In cases that controller has some autowired objects like service or repository objects. you can simply mock them with annotation #MockBean as you can see above code.
See this link for other test slice annotations spring provided
For projects with org.springframework.boot:spring-boot-starter-test can use
#RunWith(SpringRunner.class)
#SpringBootTest(classes = App.class)
public class ControllerGreetingTest {
...
}
Where App.class is you main application class annotated with #SpringBootApplication. But you better read the documentation. And if you don't want to include (classes = App.class) part you also can change folder structure
For simple controllers it is possible to perform simple standalone tests
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = ControllerGreeting.class)
public class ControllerGreetingTest {
#Autowired
private MockMvc mockMvc;
...
}
Just add the #ContextConfiguration annotation and refer one or more XML configuration file locations or one or more configuration classes. Otherwise Spring cannot autowire your controller, which should be tested.
Example: You want to test a controller, which uses MyService via #Autowired:
MyControllerTest: Injects the controller, which should be tested using the MyTestConfig configuration class.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {MyTestConfig.class})
#WebAppConfiguration
public class MyControllerTest {
private MockMvc mockMvc;
#Autowired
private MyController controller;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
// Tests
}
MyTestConfig: Returns all beans, which are required for the test. Use Mockito to mock the depedencies of your controller, because we want to test only the controller and not the service layer.
#Configuration
public class MyTestConfig {
#Bean
public MyService myService() {
return Mockito.mock(MyService.class);
}
#Bean
public MyController myController() {
return new MyController();
}
}

Categories