Spring Test: Create Few Classes Inside Test - spring

I have an interface without implementation classes.
public interface MyInterface {
String getName();
}
And I have another class which have MyInterface as dependency.
#Component
public class SomeClass {
#Autowired
private MyInterface implementation;
public MyInterface someMethod(List<MyInterface> list){
//logic
}
}
I have to test SomeClass, but I don't have MyInterface implementation classes. Is it possible to create in test few MyInterface implementation classes and add them to ApplicationContext when I can get them as spring beans with #Autowired?

Assuming you use JUnit 5, this is possible using #TestConfiguration and provide an implementation there:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.test.context.junit.jupiter.SpringExtension;
#ExtendWith(SpringExtension.class)
class SomeServiceTest {
#Autowired
private MyInterface myInterface;
#TestConfiguration
static class TestConfig {
#Bean
public MyInterface myInterface() {
/**
* Long version of implementing your interface:
*
* return new MyInterface() {
* #Override
* public String getName() {
* return null;
* }
* };
*/
// shorter ;-)
return () -> "Hello World!";
}
}
#Test
void test() {
SomeService someService = new SomeService(myInterface);
System.out.println(someService.doFoo());
}
}
But you could also use Mockito and create a mock for your MyInterface and have a fast unit test:
#ExtendWith(MockitoExtension.class)
class SomeServiceTest {
#Mock
private MyInterface myInterface;
#InjectMocks
private SomeService someService;
#Test
void test() {
when(myInterface.getName()).thenReturn("Test");
System.out.println(someService.doFoo());
}
}
While the code above technically answers what you asked for, always consider, as J Asgarov mentioned in the comments, if it makes sense to do it in this way.

Related

How do you configure mocks before Autowired?

I want to set up mock data using Mockito before my test. However, the autowire happens before the #Before so my tests that expect data to be present is missing. Is there a way around it?
#RunWith(SpringRunner.class)
#ContextConfiguration(
classes = {
Foo.class
}
)
public class FooTest {
#MockBean
final Programs programs;
#Autowired
final Foo foo;
#Before
public void setPrograms() {
when(programs.findAll())
.thenReturn(
List.of(
"A", "B", "C"
)
);
}
#Test
public void foo() {
assertThat(foo.getBlah()).isNotEmpty();
}
}
interface Programs {
List<String> findAll();
}
class Foo {
// I have more complicated structures than a list, for example only.
private List<String> blah;
#Autowired
private Programs programs;
public List<String> getBlah() { return blah; }
#PostConstruct
public void init() {
blah = programs.findAll();
}
}
Following your example I have developed the next ones:
public interface Programs {
List<String> findAll();
}
public class Foo {
// I have more complicated structures than a list, for example only.
private List<String> blah;
#Autowired
private Programs programs;
public List<String> getBlah() { return blah; }
#PostConstruct
public void init() {
blah = programs.findAll();
}
}
Above ones equals to your provided code. Now, "the changes":
// Only for testing purpose, you can use any of your own ones
public class ProgramsImpl implements Programs {
#Override
public List<String> findAll() {
return asList("1", "2", "3");
}
}
And the junit test:
#RunWith(SpringRunner.class)
#ContextConfiguration(
classes = {
Foo.class, ProgramsImpl.class
}
)
public class FooTest {
#SpyBean
Programs programs;
#Autowired
Foo foo;
#Before
public void prepare() {
when(programs.findAll())
.thenReturn(
List.of(
"A", "B", "C"
)
);
this.foo.init();
}
#Test
public void foo() {
assertEquals(asList("A", "B", "C"), foo.getBlah());
}
}
The above test works as expected, that is, although ProgramsImpl.findAll returns "1", "2", "3" the when clause included will overwrite it with your own one.
However, as you probably will figure out really the #PostConstruct is invoked twice: one due to #Autowire and the second one due to init.
If you want to avoid it, you need to remove #Autowire in your test and create your custom instance of foo inside #Before (using a "normal constructor"). For example:
public class Foo {
...
private Programs programs;
#Autowired
public Foo(Programs programs) {
this.programs = programs;
}
...
}
#RunWith(SpringRunner.class)
#ContextConfiguration(
classes = {
ProgramsImpl.class
}
)
public class FooTest {
#SpyBean
Programs programs;
Foo foo;
#Before
public void prepare() {
when(programs.findAll())
.thenReturn(
List.of(
"A", "B", "C"
)
);
this.foo = new Foo(programs);
this.foo.init();
}
#Test
public void foo() {
assertEquals(asList("A", "B", "C"), foo.getBlah());
}
}
As Programs is an interface; in reality it's extends CrudRepository I am able to instantiate a copy of it even a mock with a #Configuration/#Bean. This solution ensures that everything (except the mocks) are managed by Spring including the PostConstruct lifecycle.
This approach allows me to have Spring components that contain PostConstructs that use JPA without having to set up an initial database with the proper data.
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.PostConstruct;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
#RunWith(SpringRunner.class)
#ContextConfiguration(
classes = {
Foo.class,
StackOverflowTest.ProgramsConfiguration.class
}
)
public class StackOverflowTest {
#Autowired
private Foo foo;
#Configuration
static class ProgramsConfiguration {
#Bean
public Programs programs() {
final Programs mock = mock(Programs.class);
when(mock.findAll()).thenReturn(List.of("A", "B", "C"));
return mock;
}
}
#Test
public void foo() {
assertThat(foo.getBlah())
.isNotEmpty()
.containsExactly("A", "B", "C");
}
}
interface Programs {
List<String> findAll();
}
class Foo {
private List<String> blah;
#Autowired
private Programs programs;
public List<String> getBlah() {
return blah;
}
#PostConstruct
public void init() {
blah = programs.findAll();
}
}
I actually found that I had solved this problem a while back when I was trying to get JOOQ working with PostConstructs as well using a MockDslContextConfiguration. Took me a bit of time to realize it was the same situation.
#Configuration
public class MockDslContextConfiguration {
#Bean
public DSLContext dslContext() {
return DSL.using(new MockConnection(mockExecuteContext -> new MockResult[]{
new MockResult(0, Mockito.mock(Result.class)),
new MockResult(0, Mockito.mock(Result.class))
}), SQLDialect.MYSQL);
}
}
Though this Q&A is more general and not limited to JOOQ.
This has a limitation of that I can only have one set of data for the test per test class.

Spring/Mockito Junit testing Null Pointer Exception

Trying to get JUnit with mockito testing to work and getting a NPE on my test here. I'm wanting to have a verify that when createRegistrant(registrationDTO) is called, saveUpdateRegistration(Registration reg) method is called right after. An issue I find strange is that on Mockito.verify(registrationServiceImpl, Mockito.times(1)).createRegistrant(registrationDTO); it is not suggesting I can test if saveUpdateRegistration(Registration reg) method was called? IntelliJ only suggests methods that are in my interface?
My test code below:
#RunWith(MockitoJUnitRunner.class)
public class RegistrationServiceImplTest {
#InjectMocks
private RegistrationServiceImpl registrationServiceImpl;
#Mock
private RegistrationDTO registrationDTO;
#Mock
private Registration registration;
#Before
public void setup() {
registrationDTO.seteId("abc123");
registrationDTO.setTech("AWS");
registrationDTO.setFirstName("Bob");
registrationDTO.setLastName("Tom");
}
#Test
public void createRegistrant() throws Exception {
// Mockito.when(registrationServiceImpl.createRegistrant(registrationDTO)).thenReturn(registrationDTO);
Mockito.when(registrationServiceImpl.createRegistrant(registrationDTO)).thenReturn(registrationDTO);
registrationServiceImpl.createRegistrant(registrationDTO);
Mockito.verify(registrationServiceImpl, Mockito.times(1)).createRegistrant(registrationDTO);
}
Here is the code I'm testing:
package com.TechODex.service;
import com.TechODex.dto.RegistrationDTO;
import com.TechODex.model.Registration;
import com.TechODex.dao.RegistrationDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
#Service
#Transactional
public class RegistrationServiceImpl implements RegistrationService {
#Autowired
private RegistrationDAO registrationDAO;
public RegistrationDTO createRegistrant(RegistrationDTO reg) {
saveUpdateRegistration(setDTOToModel(reg));
return reg;
}
public void deleteRegistrant(Long id) { deleteUserRegistrant(id); }
public List<Registration> findAllRegistrant(String tech) { return findAllRegistrantDAO(tech);}
public List<Registration> findAllTech() { return findAllTechDAO();}
private Registration setDTOToModel(RegistrationDTO dto){
Registration registration = new Registration();
registration.setFirstName(dto.getFirstName());
registration.setLastName(dto.getLastName());
registration.seteId(dto.geteId());
registration.setTech(dto.getTech());
return registration;
}
private void saveUpdateRegistration(Registration reg){ registrationDAO.save(reg);}
}
There are several issues here.
First, you do not mock RegistrationDAO. This will lead to it being null in the class under test. Next, RegistrationService is not a mock, yet you try to use when and verifyon it. Last, RegistrationDTO is mocked, so the invocations to the setters in #Before has no effect.
Your test should look something like:
#RunWith(MockitoJUnitRunner.class)
public class RegistrationServiceImplTest {
#InjectMocks
private RegistrationServiceImpl registrationServiceImpl;
#Mock
private RegistrationDAO registrationDAO;
private RegistrationDTO registrationDTO;
private Registration registration;
#Before
public void setup() {
registration = new Registration();
registration.seteId("abc123");
registration.setTech("AWS");
registration.setFirstName("Bob");
registration.setLastName("Tom");
registrationDTO = new RegistrationDTO();
registrationDTO.seteId("abc123");
registrationDTO.setTech("AWS");
registrationDTO.setFirstName("Bob");
registrationDTO.setLastName("Tom");
}
#Test
public void createRegistrant() throws Exception {
final RegistrationDTO result = registrationServiceImpl.createRegistrant(registrationDTO);
assertSame(registrationDTO, result);
verify(registrationDAO).save(eq(registration));
}
}
given that Registration has an implemented equals-method where the fields that are set are compared. If not, you need to use an ArgumentCaptor.

Mocking two different implementation of an Interface in Controller Mockito Junit test case

I have an interface(Client) with two implementation classes PClient and SClient respectively.
Interface - Client
Impl class - PCleint, SClient
I have a controller in which both the impl classes are autowired using qualifier spring boot annotation. Now i need to write JUNIT mockito test case for the controller.
I am not sure how to mock the different Impl classes which has the same interface. I tried mocking in the following way but it throws null pointer exception in the stub method of the respective mocks.
#Mock
private Client pclient
#Mock
private Client sclient
How do i differentiate mocking different impl classes such that the methods of the impl classes are called. Should i do like the below.
#Mock
private PClient pclient
#Mock
private SClient sclient
It did not work for me . Either one throws null pointer exception . Please advise.
I guess the tricks are you need to use Before to stub the return value and use #InjectMocks for the controller.
Since #Mock won't return a "real" object, it just return a "mock"(or fake:P ) one. So when you call a method of this "mock" object, it won't return the actual output from your method, instead it gives back null. That's why you need to stub the return with #Before.
See my example code below:
Client Interface
public interface Client {
String someMethod();
}
Class implemented
public class PClient implements Client {
private static final String name = "PClient";
#Override
public String someMethod() {
return name;
}
}
public class SClient implements Client{
private static final String name = "SClient";
#Override
public String someMethod() {
return name;
}
}
The Client Controller
#Controller
public class ClientController {
#Autowired
private PClient pClient;
#Autowired
private SClient sClient;
public ClientController(){
}
public String executePClient(){
return this.pClient.someMethod();
}
public String executeSClient(){
return this.sClient.someMethod();
}
public String execute(Client client){
return client.someMethod();
}
}
The test case You need to add a #Before to stub the method return.
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;
#RunWith(MockitoJUnitRunner.class)
public class ClientControllerTest {
#Mock
private PClient pclient;
#Mock
private SClient sclient;
#InjectMocks
private ClientController clientController;
#Before
public void setup(){
when(pclient.someMethod()).thenReturn(new PClient().someMethod());
when(sclient.someMethod()).thenReturn(new SClient().someMethod());
}
#Test
public void testPClient(){
String result = clientController.executePClient();
assertEquals("PClient",result);
}
#Test
public void testSClient(){
String result = clientController.executeSClient();
assertEquals("SClient",result);
}
#Test
public void testExecuteWithPClient(){
String result = clientController.execute(pclient);
assertEquals("PClient",result);
}
#Test
public void testExecuteWithSClient(){
String result = clientController.execute(sclient);
assertEquals("SClient",result);
}
}
Result: Tests passed: 4 of 4 tests - 31ms

How can I autowire a specific implementation of an interface by its bean name in Spring

I have classes which implements MyInterface and their names are for example:
MyClassA, MyClassB etc.
How can I get the instance of the class by it's bean name? Something like:
context.getBean("myClassA")
context.getBean("myClassB")
Can I do it without configuring beans in the XML?
I want to use annotations
You can use qualifiers, e.g:
#Component
#Qualifier("classA")
public MyInterface ClassA {
return new ClassA();
}
#Component
#Qualifier("classB")
public MyInterface ClassB {
return new ClassB();
}
and use it like:
public class SomeClass {
#Autowired
#Qualifier("classA")
private MyInterface classA;
}
You have several options here. The easiest one would be using field names as a component name using #Autowire:
#Component("testClassA") // It is possible to omit explicit bean name declaration here since Spring will use a class name starting from lower case letter as a bean name by default. So just `#Component` should be sufficient here and below.
public TestClassA implements MyInterface {
}
#Component("testClassB")
public TestClassB implements MyInterface {
}
/*
* Note that field names are the same as the component names.
*/
#Component
public class TestClassWithDependencies {
#Autowired
private MyInterface testClassA;
#Autowired
private MyInterface testClassB;
}
Another option is to use qualifiers:
#Component
#Qualifier("testClassA")
public TestClassA implements MyInterface {
}
#Component
#Qualifier("testClassB")
public TestClassB implements MyInterface {
}
#Component
public class TestClassWithDependencies {
#Autowired
#Qualifier("testClassA")
private MyInterface testClassA;
#Autowired
#Qualifier("testClassB")
private MyInterface testClassB;
}
You could even create your own meta-annotations when you need to use the same qualifiers over and over again:
#Target({ElementType.FIELD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#Qualifier("testClassA")
public #interface TestClassACustomQualifier {
String value();
}
#Target({ElementType.FIELD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#Qualifier("testClassB")
public #interface TestClassBCustomQualifier {
String value();
}
#Component
public class TestClassWithDependencies {
#Autowired
#TestClassACustomQualifier
private MyInterface testClassA;
#Autowired
#TestClassBCustomQualifier
private MyInterface testClassB;
}
Much prettier, isn't it? One more option is to use #Resource from JSR-250 specification. As pointed out by #hovanessyan it's more JavaEE style of doing things, but still, I think it's a viable approach used on many projects:
#Component("testClassA")
public TestClassA implements MyInterface {
}
#Component("testClassB")
public TestClassB implements MyInterface {
}
#Component
public class TestClassWithDependencies {
#Resource(name="testClassA")
private MyInterface testClassA;
#Resource(name="testClassB")
private MyInterface testClassB;
}
More information you can get on https://www.sourceallies.com/2011/08/spring-injection-with-resource-and-autowired/, where discussed different approaches with tests added.
Hope this helps!
I think if above options don't suffice then factory implementation is one way to get instance on the fly -
#Component
public TestClassA implements MyInterface {
}
#Component
public TestClassB implements MyInterface {
}
define you factory this way -
public class MyInterfaceFactory extends AbstractFactoryBean<MyInterface> {
private String filter;
#Override
public Class<?> getObjectType() {
return MyInterface.class;
}
#Override
protected MyInterface createInstance() throws Exception {
MyInterface myInterface;
switch (filter)
{
case "1":
myInterface = new TestClassA();
break;
case "2":
myInterface = new TestClassB();
break;
default: throw new IllegalArgumentException("No such type.");
}
return myInterface;
}
}
and then your bean config -
#Configuration
public class FactoryBeanConfig {
#Bean(name = "myInterface")
public MyInterfaceFactory myInterfaceFactory() {
MyInterfaceFactory factory = new MyInterfaceFactory();
factory.setFilter("7070");
return factory;
}
}

How to create a mocked (by jmockit) spring bean?

I am new to jmockit and would like to mock a bean inside my Java based Spring Application Configuration. I thought (better hoped) it would go like this:
#Configuration
public class MyApplicationConfig {
#Bean // this bean should be a mock
SomeService getSomeService() {
return new MockUp<SomeService>() {#Mock String someMethod() { return ""; }}.getMockInstance();
}
#Bean // some other bean that depends on the mocked service bean
MyApplication getMyApplication(SomeService someService) {
....
}
}
But unfortunatly this fails with "Invalid place to apply a mock-up".
I wonder if I can generate jmockit mocks inside Spring Configuration classes at all. I need the bean because it is referenced by other beans and the whole Spring Context initialization fails if I do not provide the mock as a Spring bean.
Thanks for any help.
Just use your regular Spring configuration. In a test class, declare the type to be mocked with #Capturing. It will mock whatever the implementation class that Spring used.
Edit: added full example code below.
import javax.inject.*;
public final class MyApplication {
private final String name;
#Inject private SomeService someService;
public MyApplication(String name) { this.name = name; }
public String doSomething() {
String something = someService.doSomething();
return name + ' ' + something;
}
}
public final class SomeService {
public String getName() { return null; }
public String doSomething() { throw new RuntimeException(); }
}
import org.springframework.context.annotation.*;
#Configuration
public class MyRealApplicationConfig {
#Bean
SomeService getSomeService() { return new SomeService(); }
#Bean
MyApplication getMyApplication(SomeService someService) {
String someName = someService.getName();
return new MyApplication(someName);
}
}
import javax.inject.*;
import org.junit.*;
import org.junit.runner.*;
import static org.junit.Assert.*;
import mockit.*;
import org.springframework.test.context.*;
import org.springframework.test.context.junit4.*;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = MyRealApplicationConfig.class)
public final class MyApplicationSpringTest {
#Inject MyApplication myApplication;
#Mocked SomeService mockService;
#BeforeClass // runs before Spring configuration
public static void setUpMocksForSpringConfiguration() {
new MockUp<SomeService>() {
#Mock String getName() { return "one"; }
};
}
#Test
public void doSomethingUsingMockedService() {
new Expectations() {{ mockService.doSomething(); result = "two"; }};
String result = myApplication.doSomething();
assertEquals("one two", result);
}
}
import org.junit.*;
import static org.junit.Assert.*;
import mockit.*;
// A simpler version of the test; no Spring.
public final class MyApplicationTest {
#Tested MyApplication myApplication;
#Injectable String name = "one";
#Injectable SomeService mockService;
#Test
public void doSomethingUsingMockedService() {
new Expectations() {{ mockService.doSomething(); result = "two"; }};
String result = myApplication.doSomething();
assertEquals("one two", result);
}
}
Spring-ReInject is designed to replace beans with mocks.

Resources