Spring boot create multiple mocks of the same interface doesn't work with constructor - spring-boot

I try to implement MockitoExtension in Spring Boot with a simple service class. When I implement it via field-set, it works. But when I implement it constructor, i doesn't work.
ExampleService:
#Service
public class ExampleService
{
private final AmqpTemplate requestTemplate;
private final AmqpTemplate resultTemplate;
public ExampleService(#Qualifier("requestTemplate") AmqpTemplate requestTemplate,
#Qualifier("resultTemplate") AmqpTemplate resultTemplate)
{
this.requestTemplate = requestTemplate;
this.resultTemplate = resultTemplate;
}
}
ExampleServiceTest:
#ExtendWith(MockitoExtension.class)
public class ExampleServiceTest
{
#InjectMocks
private ExampleService exampleService;
#Mock(name = "requestTemplate")
private AmqpTemplate requestTemplate;
#Mock(name = "resultTemplate")
private AmqpTemplate resultTemplate;
#Test
public void test1()
{
...
}
}
requestTemplate and resultTemplate looks like same object on test cases and it behaves wrong...
When I implement it via Autowired, it works...
#Service
public class ExampleService
{
#Autowired
private AmqpTemplate requestTemplate;
#Autowired
private AmqpTemplate resultTemplate;
...
}
I wonder the constructor solution and reason why it doesn't work.

Related

MapStruct mapper not initialized with autowired when debug

I use spring boot 2.3.2 with mapstruct.
In a service class I have a mapper who have an autowired annotation.
#Service
public BillingService{
private BillingRepository billingRepository;
#Autowired
private BillingMapper billingMapper;
#Autowired
public BillingService (BillingRepository billingRepository){
this.billingRepository=billingRepository;
}
public void getBilling(Long billingId){
....
billingMapper.convertTo(...);
}
}
#RunWith(MockitoJUnitRunner.class)
public class BillingServiceTest{
#Mock
BillingRepository billingRepository;
private BillingService bilingService;
#Spy
private BillingMapper billingMapper = Mappers.getMapper(BillingMapper.class);
#BeforeEach
public void setup(){
MockitoAnnotations.initMocks(this);
billingService = new BillingService();
}
#Test
public void testGetBilling(){
List<Billing> billings = new ArrayList<>();
...
List<BillingPayload> payloads = new ArrayList<>();
when(billingMapper.convertTo(billings)).thenReturn(payloads);
bilingService.getBilling(1l);
}
}
#Mapper(componentModel="spring")
public interface BillingMapper{
...
}
When I debug and I'm stuck in getBilling method in BillingService Class, billingMapper is alway null;
You are using very strange configuration. First of all you have method returning BillingService that doesn't specify any return value so this would not even compile. I suggest following way:
#Service
public BillingService{
private final BillingRepository billingRepository;
private final BillingMapper billingMapper;
// constructor with bean injection
public BillingService(final BillingRepository billingRepository,
final BillingMapper billingMapper) {
this.billingRepository = billingRepository;
this.billingMapper = billingMapper;
}
public void getBilling(Long billingId){
....
billingMapper.convertTo(...);
}
}
Then you can configure your test like following:
#RunWith(SpringJUnit4ClassRunner.class)
public class BillingServiceTest {
#Spy private BillingMapper billingMapper = Mappers.getMapper(BillingMapper.class);
#MockBean private BillingRepository billingRepository;
#Autowired private BillingService billingService;
#TestConfiguration
static class BillingServiceTestContextConfiguration {
#Bean public BillingService billingService() {
return new BillingService(billingRepository, billingMapper);
}
}
#Test
public void testGetBilling(){
List<Billing> billings = new ArrayList<>();
...
List<BillingPayload> payloads = new ArrayList<>();
when(billingRepository.findById(1L)).thenReturn(); // someResult
when(billingMapper.convertTo(billings)).thenReturn(payloads);
bilingService.getBilling(1l);
}
}
#Mapper(componentModel="spring")
public interface BillingMapper{
...
}
Similiar configuration should work. Main problem is that you are mixing #Autowired with setter/constructor injection (don't know since your weird method inside BillingService. Also dont know why you use #Spy annotation when you are tryning to Mock interface. #Spy is used to mock actual instance, while #Mock is mocking Class type. I would stick with #MockBean private BillingMapper billingMapper instead if BillingMapper is specified as Bean in your application.

Constructor Injection in configuration classes in Sprin5

I was trying to create a configuration class
#Configuration
public class AppConfig {
private final SomeBean someBean;
private final AnotherBean anotherBean;
#Autowired
public AppConfig(SomeBean someBean, AnotherBean anotherBean) {
this.someBean = someBean;
this.anotherBean = anotherBean;
}
}
Another approach with
#Configuration
public class AppConfig {
#Autowired
private final SomeBean someBean;
#Autowired
private final AnotherBean anotherBean;
}
Not sure which one I have to go for.
I have seen many codes with the second approach, but not with the first approach.
Any reason for this?

Spring injection: #MockBean #Repository is not injected

I'm trying to #MockBean a #Repository annotated class:
#Repository
public interface ApplicationDao extends MongoRepository<Application, String> {}
I'm injecting it into a #Service annotated class:
#Service
public class AuthorizationService {
private ApplicationDao appsDao;
private List<Application> allowedApplications;
#Autowired
public AuthorizationService(ApplicationDao appsDao) {
this.appsDao = appsDao; //<<MOCKED INJECTED BEAN>>
this.fillApplications();
}
private void fillApplications() {
this.appsDao.findAll() //<<MOCKED method>>
.forEach(entry -> {
this.allowedApplications.put(entry.getName(), entry);
});
}
public bool isAuthorized(Application application) {
return this.allowedApplications
.stream()
.anyMatch(app -> app.getId().equals(application.getId()));
}
}
My test mocking configuration looks like:
#RunWith(SpringRunner.class)
#SpringBootTest()
public class GroupReferencesTest {
private #Autowired AuthorizationService;
private #MockBean ApplicationDao applicationDao;
#Before
public void setUp() {
Application testApplication = new Application();
testApplication.setName("test-application");
List<Application> allowedApplications = new ArrayList<Application>();
allowedApplications.add(testApplication);
Mockito
.when(this.applicationDao.findAll())
.thenReturn(allowedApplications);
}
#Test
public void test() {
Application app = new Application();
app.getId("test-application");
assertTrue(this.authorizationService.isAuthorized(app)); //<<FAILS>>
}
}
Nevertheless, my mocked object is not injected. I mean, when my AuthorizationService calls its injected ApplicationDao is returns an empty list instead of my mocked list.
I've tried to use #MockBean(name="applicationDao") as well. The behavior is the same.
I've also tried to configure my mocked bean using this code:
#TestConfiguration
public class RestTemplateTestConfiguration {
#Bean("applicationDao")
#Primary
public static ApplicationDao mockApplicationDao() {
ApplicationDao mock = Mockito.mock(ApplicationDao.class);
Application testApplication = new Application();
testApplication.setName("test-application");
List<Application> allowedApplications = new ArrayList<Application>();
allowedApplications.add(testApplication);
Mockito
.when(mock.findAll())
.thenReturn(allowedApplications);
return mock;
}
}
However, it doesn't works right.
Application class is:
public class Application {
private String id;
//setters & getters
}
Any ideas?
First things first - the type of test. Answer: Unit test.
You are starting Spring context that manages a lifecycle of AuthorizationService and then you are trying to inject mock. What really happens is that Spring IoC container is injecting a real ApplicationDao (the one managed by Spring IoC container) into the AuthorizationService.
Solution:
Manage lifecyle of AuthorizationService by your test runner (like MockitoJUnitRunner and inject ApplicationDao mock into it):
#RunWith(MockitoJUnitRunner.class)
public class GroupReferencesTest {
private #InjectMocks AuthorizationService authorizationService;
private #Mock ApplicationDao applicationDao;
#Before
public void setUp() {
Application testApplication = new Application();
testApplication.setName("test-application");
List<Application> allowedApplications = new ArrayList<Application>();
allowedApplications.add(testApplication);
Mockito
.when(this.applicationDao.findAll())
.thenReturn(allowedApplications);
}
#Test
public void test() {
Application app = new Application();
app.getId("test-application");
assertTrue(this.authorizationService.isAuthorized(app));
}
}
Working example
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {AuthorizationService.class})
public class GroupReferencesTest {
#Autowired
private AuthorizationService;
#MockBean
private ApplicationDao applicationDao;
#Test
public void test() {
//given
Mockito.when(applicationDao.findAll()).thenReturn(emptyList());
//when & then
assertTrue(authorizationService.isAuthorized(app));
}
}

Spring boot autowiring an interface with multiple implementations

In normal Spring, when we want to autowire an interface, we define it's implementation in Spring context file.
What about Spring boot?
how can we achieve this?
currently we only autowire classes that are not interfaces.
Another part of this question is about using a class in a Junit class inside a Spring boot project.
If we want to use a CalendarUtil for example, if we autowire CalendarUtil, it will throw a null pointer exception. What can we do in this case? I just initialized using "new" for now...
Use #Qualifier annotation is used to differentiate beans of the same interface
Take look at Spring Boot documentation
Also, to inject all beans of the same interface, just autowire List of interface
(The same way in Spring / Spring Boot / SpringBootTest)
Example below:
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
public interface MyService {
void doWork();
}
#Service
#Qualifier("firstService")
public static class FirstServiceImpl implements MyService {
#Override
public void doWork() {
System.out.println("firstService work");
}
}
#Service
#Qualifier("secondService")
public static class SecondServiceImpl implements MyService {
#Override
public void doWork() {
System.out.println("secondService work");
}
}
#Component
public static class FirstManager {
private final MyService myService;
#Autowired // inject FirstServiceImpl
public FirstManager(#Qualifier("firstService") MyService myService) {
this.myService = myService;
}
#PostConstruct
public void startWork() {
System.out.println("firstManager start work");
myService.doWork();
}
}
#Component
public static class SecondManager {
private final List<MyService> myServices;
#Autowired // inject MyService all implementations
public SecondManager(List<MyService> myServices) {
this.myServices = myServices;
}
#PostConstruct
public void startWork() {
System.out.println("secondManager start work");
myServices.forEach(MyService::doWork);
}
}
}
For the second part of your question, take look at this useful answers first / second
You can also make it work by giving it the name of the implementation.
Eg:
#Autowired
MyService firstService;
#Autowired
MyService secondService;
Assume that you have a GreetingService
public interface GreetingService {
void doGreetings();
}
And you have 2 implementations HelloService
#Service
#Slf4j
public class HelloService implements GreetingService{
#Override
public void doGreetings() {
log.info("Hello world!");
}
}
and HiService
#Slf4j
#Service
public class HiService implements GreetingService{
#Override
public void doGreetings() {
log.info("Hi world!");
}
}
Then you have another interface, which is BusinessService to call some business
public interface BusinessService {
void doGreetings();
}
There are some ways to do that
#1. Use #Autowired
#Component
public class BusinessServiceImpl implements BusinessService{
#Autowired
private GreetingService hiService; // Spring automatically maps the name for you, if you don't want to change it.
#Autowired
private GreetingService helloService;
#Override
public void doGreetings() {
hiService.doGreetings();
helloService.doGreetings();
}
}
In case you need to change your implementation bean name, refer to other answers, by setting the name to your bean, for example #Service("myCustomName") and applying #Qualifier("myCustomName")
#2. You can also use constructor injection
#Component
public class BusinessServiceImpl implements BusinessService {
private final GreetingService hiService;
private final GreetingService helloService;
public BusinessServiceImpl(GreetingService hiService, GreetingService helloService) {
this.hiService = hiService;
this.helloService = helloService;
}
#Override
public void doGreetings() {
hiService.doGreetings();
helloService.doGreetings();
}
}
This can be
public BusinessServiceImpl(#Qualifier("hiService") GreetingService hiService, #Qualifier("helloService") GreetingService helloService)
But I am using Spring Boot 2.6.5 and
public BusinessServiceImpl(GreetingService hiService, GreetingService helloService)
is working fine, since Spring automatically get the names for us.
#3. You can also use Map for this
#Component
#RequiredArgsConstructor
public class BusinessServiceImpl implements BusinessService {
private final Map<String, GreetingService> servicesMap; // Spring automatically get the bean name as key
#Override
public void doGreetings() {
servicesMap.get("hiService").doGreetings();
servicesMap.get("helloService").doGreetings();
}
}
List also works fine if you run all the services. But there is a case that you want to get some specific implementation, you need to define a name for it or something like that. My reference is here
For this one, I use #RequiredArgsConstructor from Lombok.
As mentioned in the comments, by using the #Qualifier annotation, you can distinguish different implementations as described in the docs.
For testing, you can use also do the same. For example:
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyClassTests {
#Autowired
private MyClass testClass;
#MockBean
#Qualifier("default")
private MyImplementation defaultImpl;
#Test
public void givenMultipleImpl_whenAutowiring_thenReturnDefaultImpl() {
// your test here....
}
}
There are 2 approaches when we have autowiring of an interface with multiple implementations:
Spring #Primary annotation
In short it tells to our Spring application whenever we try to autowire our interface to use that specific implementation which is marked with the #Primary annotation. It is like a default autowiring setting. It can be used only once per cluster of implementations of an interface. → #Primary Docs
Spring #Qualifier annotation
This Spring annotation is giving us more control to select the exact implementation wherever we define a reference to our interface choosing among its options. → #Qualifier Docs
For more details follow the links to their documentation.
public interface SomeInterfaces {
void send(String message);
String getType();
}
kafka-service
#Component
public class SomeInterfacesKafkaImpl implements SomeInterfaces {
private final String type = "kafka";
#Override
public void send(String message) {
System.out.println(message + "through Kafka");
}
#Override
public String getType() {
return this.type;
}
}
redis-service
#Component
public class SomeInterfacesRedisImpl implements SomeInterfaces {
private final String type = "redis";
#Override
public void send(String message) {
System.out.println(message + "through Redis");
}
#Override
public String getType() {
return this.type;
}
}
master
#Component
public class SomeInterfacesMaster {
private final Set<SomeInterfaces> someInterfaces;
public SomeInterfacesMaster(Set<SomeInterfaces> someInterfaces) {
this.someInterfaces = someInterfaces;
}
public void sendMaster(String type){
Optional<SomeInterfaces> service =
someInterfaces
.stream()
.filter(service ->
service.getType().equals(type)
)
.findFirst();
SomeInterfaces someService =
service
.orElseThrow(() -> new RuntimeException("There is not such way for sending messages."));
someService .send(" Hello. It is a letter to ....");
}
}
test
#SpringBootTest
public class MultiImplementation {
}
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
class SomeInterfacesMasterTest extends MultiImplementation {
#Autowired
private SomeInterfacesMaster someInterfacesMaster;
#Test
void sendMaster() {
someInterfacesMaster.sendMaster("kafka");
}
}
Thus, according to the Open/Closed principle, we only need to add an implementation without breaking existing code.
#Component
public class SomeInterfacesRabbitImpl implements SomeInterfaces {
private final String type = "rabbit";
#Override
public void send(String message) {
System.out.println(message + "through Rabbit");
}
#Override
public String getType() {
return this.type;
}
}
test-v2
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
class SomeInterfacesMasterTestV2 extends MultiImplementation {
#Autowired
private SomeInterfacesMaster someInterfacesMaster;
#Test
void sendMasterV2() {
someInterfacesMaster.sendMaster("rabbit");
}
}
If we have multiple implementations of the same interface, Spring needs to know which one it should be autowired into a class. Here is a simple example of validator for mobile number and email address of Employee:-
Employee Class:
public class Employee {
private String mobileNumber;
private String emailAddress;
...
/** Getters & Setters omitted **/
}
Interface EmployeeValidator:
public interface EmployeeValidator {
public Employee validate(Employee employee);
}
First implementation class for Mobile Number Validator:
#Component(value="EmployeeMobileValidator")
public class EmployeeMobileValidator implements EmployeeValidator {
#Override
public Employee validate(Employee employee) {
//Mobile number Validation logic goes here.
}
}
Second implementation class for Email address Validator:
#Component(value="EmployeeEmailValidator")
public class EmployeeEmailValidator implements EmployeeValidator {
#Override
public Employee validate(Employee employee) {
//Email address validation logic goes here.
}
}
We can now autowired these above validators individually into a class.
Employee Service Interface:
public interface EmployeeService {
public void handleEmployee(Employee employee);
}
Employee Service Implementation Class
#Service
public class EmployeeServiceImpl implements EmployeeService {
/** Autowire validators individually **/
#Autowired
#Qualifier("EmployeeMobileValidator") // Autowired using qualifier for mobile validator
private EmployeeValidator mobileValidator;
#Autowired
#Qualifier("EmployeeEmailValidator") // Autowired using qualifier for email valodator
private EmployeeValidator emailValidator;
#Override
public void handleEmployee(Employee employee) {
/**You can use just one instance if you need**/
employee = mobileValidator.validate(employee);
}
}

Spring MVC Repository Factory

Approach 1:
Below are the two service classes which are using same 2 repositories.
#org.springframework.stereotype.Service(value = "userService")
public class UserServiceImpl implements UserService {
#Autowired
private CounterRepository counterRepository;
#Autowired
private SessionRepository sessionRepository;
}
#org.springframework.stereotype.Service(value = "projectService")
public class UserServiceImpl implements UserService {
#Autowired
private CounterRepository counterRepository;
#Autowired
private SessionRepository sessionRepository;
}
So in above classes, as you see that CounterRepository & SessionRepository are using two times each in UserServiceImpl & ProjectServiceImpl services.
Is this is correct approach or I can make One Factory Class and use it to get required repo.
Approach 2:
class RepoFactory{
#Autowired
private CounterRepository counterRepository;
#Autowired
private SessionRepository sessionRepository;
public <T> T getRepo(Class<T> entityClass) {
if (entityClass == CounterRepository .class) {
return (T) appMessageRepository;
} else if (entityClass == SessionRepository.class) {
return (T) auditTrailRepository;
}
}
And I use like below
#org.springframework.stereotype.Service(value = "userService")
public class UserServiceImpl implements UserService {
#Autowired
private RepoFactory repoFactory;
public void someMethod(){
repoFactory.getRepo(CounterRepository.class);
.....
}
public void someMethod2(){
repoFactory.getRepo(SessionRepository.class);
.....
}
}
#org.springframework.stereotype.Service(value = "projectService")
public class ProjectServiceImpl implements ProjectService {
#Autowired
private RepoFactory repoFactory;
public void someMethod(){
repoFactory.getRepo(CounterRepository.class);
.....
}
public void someMethod2(){
repoFactory.getRepo(SessionRepository.class);
.....
}
}
Could you please help me out which approach is better according to performance and memory consumption.
If you don't configure it explictly, all spring beans are singleton, it will not affect memory at all! http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-factory-scopes
The first approach is easy to go, recommended!!
The second approach is totally unnecessary, if you want to, you can always autowire applicationContext and use applicationContext.getBean(Mybean.class)
If you have to save some memory on application start, you can have a look at #Lazy, but from my point of view, it is also not necessary.

Resources