there's a problem here!
In my application I have a Consumer (kafka) that uses a Facade that uses a Client that calls a rest Api with RestTemplate
I want (have) to test the whole flow excluded the rest call. so I've tried to mock RestTemplate but without success -_-
here an example of my classes
#Service
public class KafkaConsumer{
#Autowired MyFacade facade;
#KafkaListener
public void onMessage(){
facade.awesomeMethod();
}
}
#Service
public class MyFacade{
#Autowired MyClient client;
public void awesomeMethod(){
client.callApi();
}
}
#Service
public class MyClient{
#Autowired RestTemplate restTemplate;
public void callApi(){
restTemplate.exchange(.....);
}
}
now I'm tring to build a test with mock but something goes wrong and RestTemplate call real server
here test class
#ActiveProfile("test")
#RunWith(SpringRunner.class)
#SpringbootTest
#EmbeddedKafka
public class MyFailTest{
#Mock RestTemplate restTemplate;
#InjectMocks MyFacade facade;
#InjectMocks MyClient client;
#InjectMocks KafkaConsumer consumer;
#Autowired KafkaProducer producer;
#Test
public void test(){
Mokito.when(restTemplate.exchange(....)).thenReturn(new ResponseEntity<Void>(HttpStatus.OK);)
producer.send("My Kafka Message");
}
}
here my test dependecies
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.11.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.4.0</version>
<scope>test</scope>
</dependency>
Junit4 and Springboot 2.6.7
I think I'm failing in #Mock and #InjecMock logic, i've tried some combination of them ; ) but without success
thanks to #geobreze comment I drilled down into #MockBean.
It was a way tried on my thousands tries but I copied some code without really understand what it was coping
At the end I've used #SpyBean because #MockBean mocks all methods class. To semplify implementation I've mocked Client object, exchenge of RestTemplate it is a little bit complex, I didn't want fall in other stupid error.
It is not useful declare all classes chain, so I've removed facade and I've used #autowired annotation rather than #InjectMock
here my test
#ActiveProfile("test")
#RunWith(SpringRunner.class)
#SpringbootTest
#EmbeddedKafka
public class MySuccessfullTest{
#SpyBean MyClient client;
#Autowired KafkaConsumer consumer;
#Autowired KafkaProducer producer;
#Test
public void test(){
MyClientResponse responseObject = MyClientResponse();
Mokito.doReturn(responseObject).when(client).callApi();
producer.send("My Kafka Message");
}
}
Related
I'm looking for a unit test for Controllers using Mockito and PowerMock. Every controller has only private void methods with a single argument and a #Autowired service dependency:
public class ProjectController {
private ProjectServiceImpl service;
#Autowired
public void setInjectedBean(ProjectServiceImpl service) {
this.service = service;
}
private void createProject(String someString) {
// do stuff by calling service.doSomething(someString)
}
}
ProjectServiceImpl is annotated with #Service and has only a public void method with a String as argument. As it is too simple, I didn't bother providing the code.
My last attempt to accomplish that test looks like this:
#RunWith(PowerMockRunner.class)
#ExtendWith(MockitoExtension.class)
#PrepareForTest({ProjectController.class, ProjectServiceImpl.class})
class ProjectControllerTest {
#InjectMocks
private ProjectController controller;
#Mock
private ProjectServiceImplservice;
#Test
void createProject() throws Exception {
controller = PowerMockito.spy(new ProjectController());
PowerMockito.doNothing().when(controller, "createProject", new String(""));
}
}
Question 1: is it possible to make PowerMock work aside of Mockito?
Question 2: using PowerMock, what's the most appropriate way of calling for a private void method with argument?
IMPORTANT: Before marking this post as a duplicate, I personally challenge you finding any content how to do so. It seems like there is just no content showing not only how to make both work together, but make PowerMockito calls for a private void method with one or more arguments.
dependencies used for PowerMock:
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-core</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
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>
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
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();
}
}
I am new to spring boot. Need some suggestions
Here my unit test class
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = DemoApplication.class)
public class EmployeeRepositoryTest {
#Autowired
protected EmployeeRepository employeeRepository;
#Test
public void insertEmploee(){
Employee employee = new Employee();
employee.setEmpName("Azad");
employee.setEmpDesignation("Engg");
employee.setEmpSalary(12.5f);
employeeRepository.save(employee);
}
}
When I run it I get exception as
java.lang.NoSuchMethodError: org.springframework.core.annotation.AnnotatedElementUtils.findMergedAnnotationAttributes(Ljava/lang/reflect/AnnotatedElement;Ljava/lang/String;ZZ)Lorg/springframework/core/annotation/AnnotationAttributes;
at org.springframework.test.util.MetaAnnotationUtils$AnnotationDescriptor.<init>(MetaAnnotationUtils.java:290)
at org.springframework.test.util.MetaAnnotationUtils$UntypedAnnotationDescriptor.<init>(MetaAnnotationUtils.java:365)
at org.springframework.test.util.MetaAnnotationUtils$UntypedAnnotationDescriptor.<init>(MetaAnnotationUtils.java:360)
at org.springframework.test.util.MetaAnnotationUtils.findAnnotationDescriptorForTypes(MetaAnnotationUtils.java:191)
at org.springframework.test.util.MetaAnnotationUtils.findAnnotationDescriptorForTypes(MetaAnnotationUtils.java:198)
at
Process finished with exit code -1
It seems that your problem is solved (mixing the Spring dependency versions) but let me just expand the comment from #g00glen00b on how to write unit tests.
Make sure the following dependency is in your pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
As pointed out in the comment, #RunWith(SpringJUnit4ClassRunner.class) causes the unit test to start the whole application and it is used rather for integration testing.
Fortunately, Spring-boot has built in dependency for Mockito which is just what you need for unit tests like this.
Now, your unit test could look something like this:
public class EmployeeRepositoryTest {
#InjectMocks
private EmployeeRepository employeeRepository;
#Mock
private Something something; // some class that is used inside EmployRepository (if any) and needs to be injected
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void insertEmploee(){
Employee employee = new Employee();
employee.setEmpName("Azad");
employee.setEmpDesignation("Engg");
employee.setEmpSalary(12.5f);
employeeRepository.save(employee);
Mockito.verify(...); // verify what needs to be verified
}
}
Nice post about using Mockito can be found, for example, here.
Instead of using #Autowired on EmployeeRepository we can use #MockBean cause we are writing unit tests we don't need to deal with the real data we just need to verify that the function is working fine or not. Check the below code
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = Main.class)//main springboot class
#WebAppConfiguration
public abstract class AbstractBaseTest {
protected MockMvc mvc;
#Autowired
WebApplicationContext webApplicationContext;
protected void setUp() {
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
}
public class EmployeeRepositoryTest extends AbstractBaseTest{
#MockBean
protected EmployeeRepository employeeRepository;
#Override
#Before
public void setUp() {
super.setUp();
}
#Test
public void insertEmploee(){
Employee employee = new Employee();
employee.setEmpName("Azad");
employee.setEmpDesignation("Engg");
employee.setEmpSalary(12.5f);
Mockito.doNothing().when(employeeRepository).save(Mockito.any(Employee.class));
employeeRepository.save(employee);
Mockito.verify(employeeRepository, Mockito.times(1)).save(employee);
}
}