Spring - Set autowired object in another class for junit tests - spring

I have a class Controller:
Question is in the comments of Test class- how should I mock myFactory object and set this object as a property of controller? Even if I mock the factory object using #Mock/ EasyMock, how should I set this in controller as with Autowiring, it does not have the setter for factory
So, I cant use controller.setFactory()
Also, How do I mock the methods in factory object , the way I do it for objects with #Mock : Mockito.when(factory.someFactoryMethod).andReturn("abc")
public class Controller{
#Autowired
MyFactory factory;
someMethod(){
String abh = factory.someFactoryMethod("defaultObject");
}
}
public class MyFactory{
public String someFactoryMethod(String argument){
//some code
}
}
public class ControllerTest{
#Before
public void initialize() {
Controller testController = spy(new Controller());
/*
how should I mock myFactory object and set this object as a property of controller? Even if I mock the factory object using #Mock/ EasyMock, how should I set this in controller as with Autowiring, it does not have the setter for factory
So, I cant use controller.setFactory(<mocked object>)
Also, How do I mock the methods in factory object , the way I do it for objects with #Mock : Mockito.when(factory.someFactoryMethod).andReturn("abc")
*/
}
}

I'd suggest at least autowiring on constructor lvl:
private final MyFactory factory;
#Autowired
public Controller(MyFactory factory) {
this.factory = factory;
}
Than in tests you can mock (via Mockito or create your own) or pass a concrete instance to the Controller:
#Before
public void initialize() {
private Controller testController = new Controller(factory);
}
To mock factory and use the given you specified you can mock it like that:
MyFactory factory = Mockito.mock(MyFactory.class);
EDIT (using annotations)
This test class could also be an example for you for using annotation based mocking from Mockito: https://github.com/gmaslowski/spock-groovy-sample/blob/master/src/test/java/com/gmaslowski/spock/sample/DummyServiceTest.java

You could use #InjectMocks in conjunction with #Spy
public class ControllerTest{
#Mock
MyFactory factory;
#Spy
#InjectMocks
Controller testController;
#Before
public void initialize() {
//expectations
}
}

Related

NPE when not injecting mock data for mapper class (spring boot)

I am using spring boot. I have the below mapper class to map the entity object to model object.
#Mapper(componentModel = "spring")
public interface PersonMapper {
PersonEntity personModelToPersonEntity(PersonModel pm)
PersonModel personEntityToPersonModel(PersonEntity pe)
}
Service class:
#Service
public class MyService{
#Autowired
PersonMapper personMapper;
#Autowired
PersonRepository personRepo;
#Transactional
public List<Person> getDetails() {
List<Person> personList = personRepo.findAll();
List<PersonModel> pm = personMapper.personEntityToPersonModel(personList);
...
...
}
}
I have the below Junit test case, where I'm mocking the data.
#ExtendWith(MockitoExtension.class)
public class PersonTest{
#InjectMocks
PersonService personService;
// #Mock
// PersonMapper personMapper;
#Mock
PersonRepository personRepo;
#Test
void getPersonDetailsTest() {
given(personRepo.findAll()).willReturn(mymockData);
//given(personMapper.personEntityToPersonModel(..).willReturn(..);
...
}
}
Eveything works if I mock the personMapper, but i don't want to mock the mapper class, when test case is executing, as i'm sending the mock entity data using "given(personRepo.findAll()).willReturn(mymockData);" so when it hits the service it should automatically convert the mock data sent to model object. In my case when i have commented the code for mocking mapper class, it is throwing NullPointerException in service class at the mapper object.
As #M. Denium said, nothing is there, because application context isn't raising with MockitoExtension. So, you can either mock your PersonMapper and stub its behavior or inject it manually with ReflectionTestUtils:
#ExtendWith(MockitoExtension.class)
public class PersonTest{
#InjectMocks
PersonService personService;
PersonMapper personMapper = Mappers.getMapper(PersonMapper.class);
#Mock
PersonRepository personRepo;
#Test
void getPersonDetailsTest() {
ReflectionTestUtils.setField(personService, "personMapper", personMapper);
given(personRepo.findAll()).willReturn(mymockData);
...
}
}

#Value on a field in a #Service injected with #MockBean results in null value

I have this service:
#Service
public class MyService {
#Value("${my.value}")
private String myValue;
public String someMethodWhichUsesMyValueField() {
return myValue;
}
// Also contains other methods that use some external services accessed with a `RestTemplate`
}
And this integration test:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MyApplication.class)
public class MyControllerTest {
private MockMvc myControllerMockMvc;
#Autowired
private MyController myController; // This controller injects an instance of MyService
#MockBean
private MyService myServiceMock;
#Before
public void setup() {
this.myControllerMockMvc = MockMvcBuilders.standaloneSetup(myController).build();
when(myServiceMock.someMethodWhichUsesMyValueField()).thenCallRealMethod();
// Also mock the other methods in myServiceMock that call the external webservices
}
#Test
public void someTest() throws Exception {
// use the myControllerMockMvc to call a POST method that calls myService.someMethodWhichUsesMyValueField()
}
}
The problem is that when myService.someMethodWhichUsesMyValueField() is called from the MyController method called from the someTest() method, the field myValue (and all the field annotated with #Autowired) is null even if my application.properties correctly defines my.value=some value.
Is there a way to correctly make myValue inject the value described in the application.properties like any normal #Autowired component?
Use a #SpyBean instead of a #MockBean because then you will have the real Spring object injected with some method that you can mock (thanks for #thomas-andolf's answer for the #Spy hint):
// [...] Nothing to change in annotations
public class MyControllerTest {
// [...] Nothing to change in the other fields
#SpyBean
private MyService myServiceMock;
#Before
public void setup() throws NoSuchFieldException, IllegalAccessException {
// [...] Nothing to change before mocking
// Then, do not mock anymore with .thenCallRealMethod() which is the default behavior with the spy
// And when mocking the other methods in myServiceMock that call the external webservices, use this kind of declaration:
doAnswer(invocation -> /* reply something */).when(myServiceMock).someMethodWhichUsesExternalWebService(any());
// instead of when(myServiceMock.someMethodWhichUsesExternalWebService(any())).thenAnswer(invocation -> /* reply something */);
}
// [...] Nothing to change in the test methods
}
The other (ugly?) way is to inject the field manually directly from the test class, like this for example:
// [...] Nothing to change in annotations
public class MyControllerTest {
// [...] Nothing to change in the other fields
#Value("${my.value}")
private String myValue;
#Before
public void setup() throws NoSuchFieldException, IllegalAccessException {
ReflectionTestUtils.setField(myServiceMock, "myValue", myValue); // thanks to #thomas-andolf's [answer](https://stackoverflow.com/a/55148170/535203)
// // Equivalent with traditional reflection tools:
// Field myValueField = MyService.class.getDeclaredField("myValue");
// myValueField.setAccessible(true);
// myValueField.set(myServiceMock, myValue);
// [...] the rest of the original method
}
// [...] Nothing to change in the test methods
}
You can set your properties in a test class
#TestPropertySource(properties = {"my.value=value"})
public class MyControllerTest {
//
}
A couple of ways you can solve this is the following, you can either use:
Constructor injection:
#Service
public class MyService {
private final myValue;
public MyService(#Value("${my.value}") myValue) {
this.myValue = myValue;
}
public String someMethodWhichUsesMyValueField() {
return myValue;
}
}
And instead of using a mock, just use the class and feed it it's value.
Or you can use:
ReflectionTestUtils.setField(myService, "myValue", "FooBar");
to set the myValue property to foobar.
Or you could change it to use a #Spy instead, which is a "partial mock". A spy is the original class with its original methods and then you can choose to mock some of the methods but keep the logic from the original class in some of the methods.

Spring #Bean at method returning already created bean

I have below class StaticDataCachesServerBean which is autowired by it's interface i.e.
#Autowired
private StaticDataCaches staticDataCaches;
in some another class
I already created instance of sdcAutoAssignmentRuleCache in init method of StaticDataCachesServerBean class. As per my understanding if annotate sdcAutoAssignmentRuleCache with #Bean won't make any impact as instance is already created and same is returned when method is called.
Is my understanding correct?
What will happen if I don't annotate sdcAutoAssignmentRuleCache() method with #Bean and what will happen if I annotate with #Bean?
#Configuration
public class StaticDataCachesServerBean implements StaticDataCaches {
#Autowired
private CerebrumApplication cerebrumApplication;
private SdcUserCache sdcUserCache;
private SdcAutoAssignmentRuleCache sdcAutoAssignmentRuleCache;
#Override
public void init() {
sdcUserCache = new SdcUserCacheImpl(
new CacheUserProviderImpl(cerebrumApplication.getCacheProvider()));
sdcAutoAssignmentRuleCache= new SdcAutoAssignmentRuleCacheImpl(
new CacheSdcAutoAssignmentRuleProviderImpl(cerebrumApplication.getCacheProvider()));
}
#Bean
#Override
public SdcAutoAssignmentRuleCache sdcAutoAssignmentRuleCache() {
return sdcAutoAssignmentRuleCache;
}
}

how to autowire spring beans from a non-bean object created at runtime?

I have some Jpa repositories and several Entity class. I need to create a helper object for one of my Entity. Inside that helper I use #Autowire to access the Jpa repositories.
#Entity
class A {
#Transient
Helper helper;
...
}
class Helper {
A a;
#Autowired
CRepository repo;
public Helper(A a) {
this.a = a;
}
}
However, the repo is always null. I've tried using SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this) and #Configurable, but both of them failed. Can anybody provide some hint for me?
BTW, A is instantiated inside a rest controller.
Thanks!.
You can use a BeanUtil class to get any bean that created in Springl
#Service
public class BeanUtil implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static <T> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
}
Then you can get the bean.
MyBean obj = BeanUtil.getBean(MyBean.class);
Use constructor injection instead of field injection; this is a best practice all the time anyway. Then it's trivial to inject your A into the controller and pass it as a constructor argument.
#Configurable annotation works fine, but you need to use #EnableSpringConfigured annotation in any configuration class in order to make it work. Read my answer in other stackoverflow post: spring autowiring not working from a non-spring managed class
Entity class should not contain any helpers, even if transient. For a clean design you need to separate concerns, so the entity should not be aware of your business logic. I cannot help you more since I don't know which is the goal of that helper, but here you have other alternatives:
ALTERNATIVE 1 (based on your description seems that helper is an stateful bean, so it is not candidate to be a #Service, which I personally think it should be)
#Controller
public MyController {
#RequestMapping(...)
public void processRequest() {
A a = new A();
...
Helper helper = new Helper(a); // CRepository is successfully autowired
}
}
#Configurable(autowire = Autowire.BY_TYPE)
public class Helper {
A a;
#Autowired
CRepository repo;
}
#Configuration
#EnableSpringConfigured
public Application {
...
}
ALTERNATIVE 2 (make your Helper class stateless so that spring is aware of your beans without the need of extra stuff like #Confgurable/#EnableSpringConfigured)
#Controller
public MyController {
#Autowired Helper helper; // CRepository is correctly autowired
#RequestMapping(...)
public void processRequest() {
A a = new A();
...
helper.doSomething(a);
}
}
#Service
public class Helper {
// A a; remove dependency to A to make it stateless
#Autowired
CRepository repo;
public Helper() {
}
public void doSomething(A a) {
...
repo.save(a);
}
}
You cannot autowire nothing in your Helper class because it isn't managed by Spring.
You can use this approach:
public class HelperManager {
#Autowired
private ApplicationContext context;
public Helper getHelper(A a) {
return context.getBean(Helper.class, a);
}
Configure Helper to be a prototype bean:
#Configuration
public class MyConfiguration {
#Bean
public HelperManager helperManager() {
return new HelperManager();
}
#Bean
#Scope("prototype")
public Helper helper(A a) {
return new Helper(a);
}
}
And finally in your controller:
#Controller
public class MyController {
#Autowired
private HelperManager helperManager;
public someMethodWhereToInstanceYourHelper(A a) {
...
Helper helper = helperManager.getHelper(a);
...
}
}

Spring autowire before constructor

Hi, I have a class that I want to test. This class has an autowired DAO object this object is been used in a #PostConstruct method, but I want to use the mock and not the real object is there a way. Here is an example:
#Autowired
PersonDao personDao;
//Constructor
public Person()
{
//Do stuff
}
#PostConstruct
void init()
{
//I need it to be a mock
personDao.add(new Person());
}
If you want to use mocked PersonDao you have several choices:
defines PersonDao mock as a Spring bean with primary="true" attribute so that it will have precedence over normal bean
move autowiring to constructor and create Person manually by providing a mock:
PersonDao personDao;
#Autowired
public Person(PersonDao personDao)
{
this.personDao = personDao;
}
then:
new Person(personDaoMock)
and don't rely on Spring.
you can modify private field using ReflectionTestUtils:
ReflectionTestUtils.setField(person, "personDao", mock);

Resources