Using Mock class with dependency injection in a JUnit test - spring

I have a basic Interface which another class is implementing.
package info;
import org.springframework.stereotype.Service;
public interface Student
{
public String getStudentID();
}
`
package info;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
#Service
public class StudentImpl implements Student
{
#Override
public String getStudentID()
{
return "Unimplemented";
}
}
I then have a service to inject that class into
package info;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
#Service
public class InfoService {
#Autowired
Student student;
public String runProg()
{
return student.getStudentID();
}
}
What I want to know is, how do I set up a JUnit test so that a Mock class of the Student interface steps in with a stubbed method instead of the method in StudentImpl. The injection does work but I want to use amock class to simulate the results instead for the sake of testing. Any help would be greatly appreciated.

In my opinion, autowiring in unit tests is a sign that's it's an integration test rather than unit test, so I prefer to do my own "wiring", as you describe. It might require you to do some refactoring of your code, but it shouldn't be a problem. In your case, I would add a constructor to InfoService that get's a Student implementation. If you wish, you can also make this constructor #Autowired, and remove the #Autowired from the student field. Spring would then still be able to autowire it, and it's also more testable.
#Service
public class InfoService {
Student student;
#Autowired
public InfoService(Student student) {
this.student = student;
}
}
Then it will be trivial to pass mocks between your services in your tests:
#Test
public void myTest() {
Student mockStudent = mock(Student.class);
InfoService service = new InfoService(mockStudent);
}

Related

jpa repositofy findall() returns empty list

I'm practicing making web pages using spring boot.
I created an h2 DB and connected it, and I want to show the name properties of the members table as a list on my web page.
I created the findall() method, but only an empty list is returned. What's wrong with my code?
my web page
MemberRepository
package com.example.testproject.store.h2.repository;
import com.example.testproject.store.h2.domain.Members;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface MemberRepository extends JpaRepository<Members, Integer> {
}
MemberService
package com.example.testproject.store.h2.service;
import com.example.testproject.store.h2.domain.Members;
import java.util.List;
public interface MemberService {
List<Members> getAll();
}
MemberServiceImpl
package com.example.testproject.store.h2.service;
import com.example.testproject.store.h2.domain.Members;
import com.example.testproject.store.h2.repository.MemberRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.lang.reflect.Member;
import java.util.List;
#Service(value = "memberServiceImpl")
public class MemberServiceImpl implements MemberService {
#Autowired
private MemberRepository memberRepository;
public List<Members> getAll(){
return memberRepository.findAll();
}
}
MemberController
package com.example.testproject.controller;
import com.example.testproject.store.h2.domain.Members;
import com.example.testproject.store.h2.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
#RestController
public class MemberController {
#Autowired
private MemberService memberService;
#GetMapping(value = "/members")
public List<Members> getAll() {
List<Members> users = memberService.getAll();
return users;
}
/*#GetMapping(value = "/members")
public List<Members> getAll() throws Exception {
return memberService.getAll();
}*/
}
Members(Entity)
package com.example.testproject.store.h2.domain;
import jakarta.persistence.*;
#Entity
#Table(name = "members")
public class Members {
#Id
private int id;
#Column
private String name;
}
I want to show the name properties of the members table as a list on my web page.
You have missed the getter setter method in entity class either you need to 1:-define getter setter method for each field or
2:-you need to add lombook dependency in pom.xml file and add #Data annootation on top of entity class so that when spring uses your entity can set and get the value from database and if you dontot want to give id explicitly then add #GeneratedValue(strategy=GenerationType.AUTO) so that spring automatically increase you id
I think you missed getters and setters in your Entity
Class just add them by using #Data annotation and try again.
You missed getters and setters for the fields in your Entity
You do not need any library, you can write it by yourself.
Otherwise, even if you use lombok, you should avoid to use #Data annotation on Entity, because it could lead to errors with default implementation of equals and hashCode. Instead you can use annotations #Getter and #Setter on the class.

How to implement the GET request using ServiceLocatorFactoryBean ( Factory Method design Pattern)

I thank you ahead for your time to read my request. I'm new to the Spring Service Locator Factory Method design Pattern and I don't understand the approach behind it. However I followed a turtorial and have been able to implement the Post request for my user registratio spring maven application. My src/main/java folder cointains this five packages:
Config
Controller
Model
Registry
Service
The Config package is to centralize the creation of users and its java class is as bellow:
package com.nidservices.yekoregistration.config;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.ServiceLocatorFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.nidservices.yekoregistration.registry.ServiceRegistry;
#Configuration
public class UserConfig {
#Bean
public FactoryBean<?> factoryBean() {
final ServiceLocatorFactoryBean bean = new ServiceLocatorFactoryBean();
bean.setServiceLocatorInterface(ServiceRegistry.class);
return bean;
}
}
The Registry package is to adapt the service base on the type of entity to create and is as bellow:
package com.nidservices.yekoregistration.registry;
public interface AdapterService<T> {
public void process(T request);
}
package com.nidservices.yekoregistration.registry;
public interface ServiceRegistry {
public <T> AdapterService<T> getService(String serviceName);
}
The Service package contains the different types of entity that inherit the User Model and the User Model is as bellow:
public class User implements Serializable {
private UUID id;
private String userIdentifier;
private String userType;
public String getUserIdentifier() {
return userIdentifier;
}
public void setUserIdentifier(String userIdentifier) {
this.userIdentifier = userIdentifier;
}
public String getUserType() {
return userType;
}
public void setUserType(String userType) {
this.userType = userType;
}
#Override
public String toString() {
return "User [userIdentifier=" + userIdentifier + ", UserType=" + userType + "]";
}
}
And the Post Request defined in the Controller is as bellow:
package com.nidservices.yekoregistration.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.nidservices.yekoregistration.model.User;
import com.nidservices.yekoregistration.registry.ServiceRegistry;
#RestController
#RequestMapping("/user")
public class UserController {
#Autowired
private ServiceRegistry serviceRegistry;
#PostMapping
public void processStudentDetails(#RequestBody User user) {
serviceRegistry.getService(user.getUserType()).process(user);
}
}
Now I'm struggling to make the GET Request to get all created users. I'm used with the DAO design pattern and very new with the concept behind ServiceLocatorFactoryBean. I appreciate your help to help me implement my CRUD endpoints using ServiceLocatorFactoryBean. Thanks in advance.

Spring Boot Unit Test #Value from .properties File gives NullPointerException

I am trying to read a value from a properties file for a unit test case in Spring Boot. I have two config.properties files, one in src/main/resources:
prop = some-value
and one in src/test/resources:
prop = some-test-value
Main Application class:
package company.division.project;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.PropertySource;
#SpringBootApplication(scanBasePackages = "company.division.project")
#PropertySource(value = "classpath:config.properties")
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
System.setProperty("DUMMY_PROPERTY", "dummy-value");
return application.sources(Application.class);
}
public static void main(String[] args) throws Exception {
// Do nothing with main
}
}
Service class to be tested:
package company.division.project.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
#Component
public class Service {
#Autowired
Environment environment;
public String getProperty() {
return environment.getProperty("prop");
}
}
ServiceTest class. I have tried two approaches to retrieving the value in the src/test/resources/config.properties file; one with an #Autowired Environment, and one with an #Value annotation...neither worked:
package company.division.project.service;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.test.context.TestPropertySource;
#RunWith(MockitoJUnitRunner.class)
#TestPropertySource("classpath:config.properties")
public class ServiceTest {
#InjectMocks
Service service;
#Autowired
Environment environment;
#Value("${prop}")
private String expectedProperty;
#Test
public void testGetPropertyWithValueAnnotation() {
assertEquals(expectedProperty, service.getProperty());
}
#Test
public void testGetPropertyWithEnvironment() {
assertEquals(environment.getProperty("prop"), service.getProperty());
}
}
I read somewhere on StackOverflow, that in order to auto-wire components in a Spring test class, I'll need to create an entire context for the test, so I tried this (change the annotations and test runner):
package company.division.project.service;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.env.Environment;
import org.springframework.test.context.junit4.SpringRunner;
#RunWith(SpringRunner.class)
#SpringBootTest
public class ServiceTest {
#InjectMocks
Service service;
#Autowired
Environment environment;
#Value("${prop}")
private String expectedProperty;
#Test
public void testGetPropertyWithValueAnnotation() {
assertEquals(expectedProperty, service.getProperty());
}
#Test
public void testGetPropertyWithEnvironment() {
assertEquals(environment.getProperty("prop"), service.getProperty());
}
}
The context was created, but both approaches ended in NullPointerExceptions once again.
The problem with your test is that you are trying to use to MockitoJUnitRunner.class in a wrong way.
If you are mocking a Service using #InjectMocks you need to make sure you need to return the value Service.getProperty() by mocking the service call. If you are using SpringRunner.class then you shouldn't have #InjectMocks but should have #Autowired for the service. Following test works.
#RunWith(SpringRunner.class)
#SpringBootTest
public class ServiceTest {
#Autowired
Service service;
#Autowired
Environment environment;
#Value("${prop}")
private String expectedProperty;
#Test
public void testGetPropertyWithValueAnnotation() {
assertEquals(expectedProperty, service.getProperty());
}
#Test
public void testGetPropertyWithEnvironment() {
assertEquals(environment.getProperty("prop"), service.getProperty());
}
}
Thanks to #shazin's answer and some of my own research I've been able to solve the problem.
Basically, there needs to be compatibility between the test runner class specified in #RunWith and the annotations for the Mockito mocks. We want to test the Service class:
Service Class:
#Component
public class Service {
#Autowired
Environment environment;
public String getProperty() {
return environment.getProperty("prop");
}
}
If you're using #RunWith(MockitoJUnitRunner.class), you can use the #InjectMocks and #Mock annotations like below. Whatever is #Autowired in Service will be auto-wired with the mocks:
Test Class with MockitoJUnitRunner:
#RunWith(MockitoJUnitRunner.class)
public class ServiceTest {
#InjectMocks
Service service;
#Mock
Environment mockEnvironment;
#Before
public void before() {
Mockito.when(mockEnvironment.getProperty("prop")).thenReturn("some-test-value")
}
}
But you can't auto-wire anything in the test class itself. That requires a Spring Context (a Spring Context is needed to manage the beans which get auto-wired into objects). That's where #RunWith(SpringRunner.class) comes into the picture. You can use it to run a test case with a dedicated Spring context (you'll notice the test case logs showing a new Spring application being booted up for every test class with the #RunWith(SpringRunner.class) annotation). You'll also need to provide the Configuration details with the #SpringBootTest annotation.
The caveat is that a test class with #RunWith(SpringRunner.class) won't understand the #InjectMocks and #Mock annotations; you'll have to use the #MockBean annotation. This will effectively modify the Spring context by replacing beans with their mocks; anything with the #Autowired annotation will get auto-wired with the mock beans automatically:
Test Class with SpringRunner:
#RunWith(SpringRunner.class)
#SpringBootTest(classes=Application.class)
public class ServiceTest {
#Autowired
Service service;
#MockBean
Environment mockEnvironment;
#Before
public void before() {
Mockito.when(mockEnvironment.getProperty("prop")).thenReturn("some-test-value")
}
}
So...using the #RunWith(SpringRunner.class) didn't achieve anything except change the names of the annotations (#InjectMocks -> #Autowired, and #Mock -> #MockBean), right? Wrong. Using SpringRunner gives you the power of auto-wiring components within your test case. So if you want to use an actual Environment (not a mock one), you can do that as well; just auto-wire it in from the dedicated Spring context:
Test Class with SpringRunner and #Autowired Environment:
#RunWith(SpringRunner.class)
#SpringBootTest(classes=Application.class)
public class ServiceTest {
#Autowired
Service service;
#Autowired
Environment environment;
#Test
public void testServiceGetProperty() {
assertEquals(environment.getProperty("prop"), service.getProperty("prop");
}
}
And that solves the problem.

Why is #Autowired working differently in application and test?

I made a sample program revealing my question:
package test;
public interface InterfaceA {
}
package test;
public class ClassA implements InterfaceA {
}
package test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class Application {
#Bean
public InterfaceA beanA() {
return new ClassA();
}
#Autowired
private ClassA beanA;
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext
= new AnnotationConfigApplicationContext(Application.class);
}
}
#Autowired doesn't work with the concrete class in this application code.
package test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = Application.class)
public class ApplicationTest {
#Autowired
ClassA beanA;
#Test
public void di() {
System.out.println(beanA);
}
}
But #Autowired works with the concrete class in this test code.
Why is #Autowired working differently in application and test?
I shared the above code at:
https://github.com/izeye/SpringTest
You're using your application context in the main entry point of the application. Make an additional 'Main' class and wire in your context like so:
#Autowired
Application context;
In your test, you're taking the time to start up the application context when you use the annotation: #ContextConfiguration(classes = Application.class).
In your concrete example you're referencing the context in the same class that you're trying to use it in. When you initialize the context in the main, the instance of Application that is running the code has already been instantiated, and did not get autowired, because at the time of its instantiation there wasn't any context for it to use.
It is a bit tricky:
Solution would be to make beanA static method and annotate Application with #Lazy or #DependsOn(value="beanA") annotations. I.e. something like this:
#Configuration
#Lazy
public class Application {
#Bean
public static InterfaceA beanA() {
return new ClassA();
}
#Autowired
private ClassA beanA;
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext
= new AnnotationConfigApplicationContext(Application.class);
}
}
Reason: for actual beanA bean creation spring need to instantiate Application class first. And while trying to resolve reference to a beanA the only information container has about this future bean is its type (InterfaceA) since there is no actual implementation exists.
However in a case of test class context was already initialized and bean can be resolved by actual implementation class which physically was already created. Same would be if you will try to reference beanA by class from any other spring component.

Managing transactions of dynamically created objects in spring

I have a web service which receives a data object(Let's call the class Student). At the web service, I wrap it using a StudentWrapper object as follows
new StudentWrapper(student)
and I want the StudentWrapper class to have methods such as save which would save the data to the database. I want to use the spring framework to annotate the save method so that it will run within a transaction. But then the StudendWrapper object would have to be a spring bean(defined in XML). If it is a spring bean, then I won't be instantiating it as I have shown above.
My question is how can I make the StudentWrapper a Spring bean (so that I can use Spring annotations to manage the transactions) but pass the Student object (that I receive over the web service) in to the StudentWrapper?
If there are any other suggestions that would help me in solving this problem, please share them as well.
If you really want to create the object using a constructor, make the StudentWrapper #Configurable and read up about using AspectJ to create prototype bean definitions for domain objects (section 9.8 of the reference manual.)
A simpler alternative, if you don't want to go with AspectJ but don't want a direct dependency on Spring is to encapsulate the prototype bean creation in a factory. I'll show you using JavaConfig, though you can do something similar in XML.
First the student object...
package internal;
public class Student {
private String name;
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
#Override
public String toString() {
return "Student{name='" + name + "'}";
}
}
And now the wrapper object...
package internal;
public class StudentWrapper {
private Student student;
public StudentWrapper(Student student) {
this.student = student;
}
public Student getStudent() {
return student;
}
#Override
public String toString() {
return "StudentWrapper{student='" + student + "'} " + super.toString();
}
}
And now the factory,
package internal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
#Component
public class StudentWrapperFactory {
#Autowired
private ApplicationContext applicationContext;
public StudentWrapper newStudentWrapper(Student student) {
return (StudentWrapper) this.applicationContext.getBean("studentWrapper", student);
}
}
And now the JavaConfig, equivalent to an XML configuration
package internal;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
#Configuration
#ComponentScan(basePackages = "internal")
public class FooConfig {
#Bean
#Scope("prototype")
public StudentWrapper studentWrapper(Student student) {
return new StudentWrapper(student);
}
}
Finally the unit test...
package internal;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {FooConfig.class})
public class FooIntegrationTest {
#Autowired
private StudentWrapperFactory studentWrapperFactory;
#Test
public void foo() {
Student student1 = new Student("student 1");
Student student2 = new Student("student 2");
StudentWrapper bean1 = this.studentWrapperFactory.newStudentWrapper(student1);
StudentWrapper bean2 = this.studentWrapperFactory.newStudentWrapper(student2);
System.out.println(bean1);
System.out.println(bean2);
}
}
produces
StudentWrapper{student='Student{name='student 1'}'} internal.StudentWrapper#1b0fa7ff
StudentWrapper{student='Student{name='student 2'}'} internal.StudentWrapper#20de643a
As you can see from the object references of StudentWrapper, they're different prototype beans. #Transactional methods should work as expected in StudentWrapper.

Resources