How to mock behaviour of a bean used in #PostConstruct in #SpringBootTest - java

I have been searching for a while and I can't find a solution for this. I need to mock a class that is used in the #PostConstruct of another class. My classes are like this:
#Component
class A {
private final B b;
private String s;
public A(B b) {this.b = b;}
#PostConstruct
public void postConstruct() {
s = b.get().getString().toUpperCase();
}
}
#Component
class B {
private final C c;
public B(C c) {this.c = c;}
public C get() {return c;}
}
#Component
class C {
public C() throws Exception {throw new Exception();}
String getString() {return "C";}
}
Now I create the test that fails because the constructor of C fails:
#SpringBootTest
class DemoApplicationTests {
#Autowired A a;
#Test
public void canLoadContext() {
assertThat(a).isNotNull();
}
}
So I try to mock the C class:
#SpringBootTest
class DemoApplicationTests {
#MockBean
static C c;
#Autowired
A a;
#BeforeEach
public void beforeEach() {
when(c.getString())
.thenReturn("C_mocked");
}
#Test
public void canLoadContext() {
assertThat(a).isNotNull();
}
}
But this way it fails with a NPE in the line s = b.get().getString().toUpperCase(); because the C object in b is not the same that I have mocked with #MockBean. When getString() is called, it returns null and we get a NPE when calling .toUpperCase().
I have also tried to overwrite the mock like this:
#Configuration
#AutoConfigureBefore
static class TestContext {
#Primary
public C c() {
return when(mock(C.class).getString())
.thenReturn("C_mocked").getMock();
}
}
But in this case I get this error:
No qualifying bean of type 'com.example.demo.A' available: expected at
least 1 bean which qualifies as autowire candidate.
Could please somebody help out with this?

I don't know how to mark it as duplicate. But please take a look of this https://stackoverflow.com/a/58517289/11538031

The solution remains like this:
#Component
class A {
private final B b;
private String s;
public A(B b) {this.b = b;}
#PostConstruct
public void postConstruct() {
s = b.get().getString().toUpperCase();
}
}
#Component
class B {
private final C c;
public B(C c) {this.c = c;}
public C get() {return c;}
}
#Component
class C {
public C() throws Exception {throw new Exception();}
String getString() {return "C";}
}
#SpringBootTest
class DemoApplicationTests {
#Autowired
A a;
#Test
public void canLoadContext() {
assertThat(a).isNotNull();
}
#TestConfiguration
public static class TestContext {
#Bean
#Primary
public C c() {
return when(mock(C.class).getString())
.thenReturn("C_mocked").getMock();
}
}
}

Related

Why is "isModifiedByPowermock returning false with #PrepareForTest?

Using Powermock 2.0.7 (powermock-api-mockito2, powermock-core, powermock-module-junit4) and mockito-core (3.3.3). I thought I had created a comparable test scenario in a separate project (which worked), but something else must be missing.
Library class to be mocked:
public class CommonConstants {
private ConfigurationDataImpl configurationData;
private static Properties sysProperties;
private static Map<String, String> sysPermissions;
public CommonConstants(ConfigurationDataImpl configurationData) {
this.configurationData = configurationData;
}
public void init() {
sysProperties = this.configurationData.getSysParams();
sysPermissions = this.configurationData.getSysPermissions();
}
public static String getSysProperties(String key) {
return sysProperties.getProperty(key);
}
public static String getSysPermissions(String key) {
return (String)sysPermissions.get(key);
}
}
In my test, I have:
#RunWith(PowerMockRunner.class)
#PrepareForTest({CommonConstants.class}) <==== class that evidently is not modified!
class MyServiceTest {
public MyServiceTest() {}
#Mock
private MyDao myDao;
#InjectMocks
private MyService myService;
#Test
void retrieveUsers() {
RequestPayload rp = returnPayload();
PowerMockito.mockStatic(CommonConstants.class); <==== EXCEPTION
when(CommonConstants.getSysProperties(HOURS_TO_REGISTER)).thenReturn("24");
...
In the service code, I have:
#Service
#Slf4j
public class MyService {
MyDao myDao;
public MyService(MyDao myDao) {
this.myDao = myDao;
}
public UserListResponse retrieveUsers(RequestPayload requestPayload, String customer) {
List<User> users = myDao.getPtdUsers(queryParams, customer, totalRecords);
int hoursToExpire = Integer.parseInt(CommonConstants.getSysProperties(HOURS__TO_REGISTER));
...
Am I leaving out something? All help appreciated.

Mock private wrapper/primitive/String field

My question is not this question.
I want to mock private wrapper fields like Integer and String.
Also, these fields are in an abstract super class.
public abstract class SuperSample
{
private Integer var1;
private String var2;
private Service service;
pubic boolean foo()
{
int a = service.doStuff(var1, var2);
return subMethod(a);
}
protected abstract boolean subMethod(int var);
public void setVar1(Integer var1)
{
this.va1 = var1;
}
public Integer getVar1()
{
return var1;
}
public void setVar2(String var2)
{
this.var2 = var2;
}
public String getVar2()
{
return var2;
}
public void setService(Service service)
{
this.service = service;
}
public String getService()
{
return service;
}
}
public class Sample extends SuperSample
{
protected boolean subMethod(int var)
{
return var%2==0?true:false;
}
}
Spring.xml -
<bean id="superSample" class="SuperSample" abstract="true">
<property name="var1" value="2" />
<property name="var2" value="cool" />
</bean>
<bean id="sample" class="Sample" >
<property name="service" ref="service" />
</bean>
In my junit I can't mock or spy var1 or var2. On spying/mocking var1, var2 I get the error:
org.mockito.exceptions.base.MockitoException: Cannot mock/spy class
java.lang.Integer Mockito cannot mock/spy following:
- final classes
- anonymous classes
- primitive types
I want to mock the line -
service.doStuff(var1, var2)
with something like-
#UnitTest
public class SampleTest
{
#Mock
private Service service;
private Integer var1 = 2
private String var2 = "cool";
#InjectMocks
private Sample sample;
#Test
public void test()
{
MockitoAnnotations.initMocks(this);
Mockito.when(service.doStuff(var1, var2)).thenReturn(5);
}
}
You can simply inject the values yourself and then remove the #InjectMocks annotation. This can best be done in a setup method annotated with #Before so it gets executed for every test.
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
sample = new Sample();
sample.setVar1(1234);
sample.setVar2("5678");
sample.setService(service);
}
Based on currently shown example the mock can be injected via the set member
#UnitTest
public class SampleTest {
#Mock
private Service service;
private Integer var1 = 2
private String var2 = "cool";
#Test
public void test() {
//Arrange
MockitoAnnotations.initMocks(this);
Sample sample = new Sample();
sample.setService(service);
sample.setVar1(var1);
sample.setVar2(var2);
Mockito.when(service.doStuff(var1, var2)).thenReturn(5);
//Act
boolean actual = sample.foo();
//Assert
//...
}
}
Ideally, the more SOLID design approach would be to refactor the subject class to use explicit dependency principle via constructor injection
public class Sample extends SuperSample {
public Sample(Service service) {
super.setService(service);
}
protected boolean subMethod(int var) {
return var%2==0?true:false;
}
}
Allowing any explicit dependencies to be injected
#UnitTest
public class SampleTest {
#Mock
private Service service;
private Integer var1 = 2
private String var2 = "cool";
#InjectMocks
private Sample sample;
#Test
public void test() {
//Arrange
MockitoAnnotations.initMocks(this);
sample.setVar1(var1);
sample.setVar2(var2);
Mockito.when(service.doStuff(var1, var2)).thenReturn(5);
//Act
boolean actual = sample.foo();
//Assert
//...
}
}
Here you go, we created a JUnit extension to solve this precise problem.
https://github.com/exabrial/mockito-object-injection

Spring circular dependance with scope PROTOTYPE

I created below scenario to understand Spring Circular dependancy.
Its clean when scope is singleton. But conflict occur when SCOPE is PROTOTYPE.
So I want to know Is Spring support avoid below scenario or should we change the design. if so please propose some design.
Consider below example.
#Service
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class BeanB {
private BeanA beanA;
#Autowired
public void setBeanA(BeanA beanA) {
this.beanA = beanA;
}
public void printBeanB() {
System.out.println(getBeanName());
}
public void printBeanBBeanA() {
System.out.println(getBeanName() + " - " + beanA.getBeanName());
}
public String getBeanName() {
return "Bean B";
}
}
#Service
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class BeanA {
private BeanB beanB;
#Autowired
public void setBeanB(BeanB beanB) {
this.beanB = beanB;
}
public void printBeanA() {
System.out.println(getBeanName());
}
public void printBeanABeanB() {
System.out.println(getBeanName() + " - " + beanB.getBeanName());
}
public String getBeanName() {
return "Bean A";
}
}
This is give an error :
Description:
The dependencies of some of the beans in the application context form
a cycle:
demoApplication (field private com.example.demo.beans.BeanA
com.example.demo.DemoApplication.beanA)
So Is Spring support avoid this or should we change the design. if so please propose some design.
I could be able to resolve this by using #Lazy annotation as below.
#Service
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class BeanA {
#Autowired
#Lazy
private BeanB beanB;
public void printBeanA() {
System.out.println(getBeanName());
}
public void printBeanABeanB() {
System.out.println(getBeanName() + " - " + beanB.getBeanName());
}
public String getBeanName() {
return "Bean A";
}
}
#Service
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class BeanB {
#Autowired
#Lazy
private BeanA beanA;
public void setBeanA(BeanA beanA) {
this.beanA = beanA;
}
public void printBeanB() {
System.out.println(getBeanName());
}
public void printBeanBBeanA() {
System.out.println(getBeanName() + " - " + beanA.getBeanName());
}
public String getBeanName() {
return "Bean B";
}
}

Mocking super class method

How to mock super method call from mocked method
public class A extends B{
#Override
public retrieveData(){
//some action here
super.retrieveData();
}
}
abstract class B extends C{
//init super fields here
}
public class C{
public String retrieveData(){
//some return
}
}
Public Atest{
#InjectMock
A a;
#Mock
C parent; //also tryed for B but the same effect
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testDetailsRetrievePageList(){
when(parent.retrieveData()).thenReturn("stub");
String s = a.retrieveListData();
Assert.assertEquals("stub", s);
}
}
Didn't help Mockito How to mock only the call of a method of the superclass and post like that.
everytime my super is null in A class

Unit testing an EJB with Mockito, TestNG and OpenEJB

I have the following EJB's:
PersonService.java
#Local
public interface PersonService {
long countPersons();
}
PersonServiceImpl.java
#Stateless
public class PersonServiceImpl implements PersonService {
#EJB
private RemotePersonService remotePersonService;
#Override
public long countPersons() {
return remotePersonService.getAllPersons().size();
}
}
RemotePersonService.java
#Local
public interface RemotePersonService {
List<Person> getAllPersons();
}
RemotePersonServiceImpl.Java
#Stateless
public class RemotePersonServiceImpl {
#Override
public List<Person> getAllPersons() {
// Here, I normally call a remote webservice, but this is for the purpose of this question
List<Person> results = new ArrayList<Person>();
results.add(new Person("John"));
return results;
}
}
And here are my tests
AbstractTest.java
public abstract class AbstractTest {
private InitialContext context;
#BeforeClass(alwaysRun = true)
public void setUp() throws Exception {
System.setProperty("java.naming.factory.initial", "org.apache.openejb.client.LocalInitialContextFactory");
Properties properties = new Properties();
properties.load(getClass().getResourceAsStream("/unittest-jndi.properties"));
context = new InitialContext(properties);
context.bind("inject", this);
}
#AfterClass(alwaysRun = true)
public void tearDown() throws Exception {
if (context != null) {
context.close();
}
}
}
PersonServiceTest.java
#LocalClient
public class PersonServiceTest extends AbstractTest {
#EJB
private PersonService personService;
#Test
public void testPersonService() {
long count = personService.countPersons();
Assert.assertEquals(count, 1l);
}
}
Now, want I want to do is replace the RemotePersonService implementation in PersonServiceImpl.java by a mock using Mockito, and still have the same call in my testPersonService method.
I tried that:
PersonServiceTest.java
#LocalClient
public class PersonServiceTest extends AbstractTest {
#Mock
private RemotePersonService remotePersonService;
#EJB
#InjectMocks
private PersonService personService;
#BeforeMethod(alwaysRun = true)
public void setUpMocks() {
MockitoAnnotations.initMocks(this);
List<Person> customResults = new ArrayList<Person>();
customResults.add(new Person("Alice"));
customResults.add(new Person("Bob"));
Mockito.when(remotePersonService.getAllPersons()).thenReturn(customResults);
}
#Test
public void testPersonService() {
long count = personService.countPersons();
Assert.assertEquals(count, 2l);
}
}
But this doesn't work. The #Mock RemotePersonService is not injected in the PersonService, and the true EJB is still used.
How can I make this work ?
Don't use annotations for your tests. Have a constructor that will wire in all your dependencies.
#Stateless
public class PersonServiceImpl implements PersonService {
#EJB
private RemotePersonService remotePersonService;
// Let your test instantiate a mock service and wire it into your test instance using this constructor.
public PersonServiceImpl(RemotePersonService rps) {
this.remotePersonService = rps;
}
#Override
public long countPersons() {
return remotePersonService.getAllPersons().size();
}
}
Create mocks and pass them to it. Your test might look like this:
#LocalClient
public class PersonServiceTest extends AbstractTest {
#Test
public void testPersonService() {
RemotePersonService mockRemotePersonService = Mockito.mock(RemotePersonService.class);
List<Person> customResults = new ArrayList<Person>();
customResults.add(new Person("Alice"));
customResults.add(new Person("Bob"));
Mockito.when(mockRemotePersonService.getAllPersons()).thenReturn(customResults);
PersonService personService = new PersonServiceImpl(mockRemotePersonService);
long count = personService.countPersons();
Assert.assertEquals(count, 2l);
}
}
I use setters on the class, and Lookup for the ejb
private ServicioAsyncMessaging servicioNotificaciones;
I delete the #EJB --> and on the getter
public ServicioAsyncMessaging getServicioNotificaciones() {
if(servicioNotificaciones == null){
servicioNotificaciones = (ServicioAsyncMessaging)Lookup.getEjb(EjbJndiConstantes.EJB_SERVICIO_ASYNC_MSG);
}
return servicioNotificaciones;
}
public void setServicioNotificaciones(ServicioAsyncMessaging servicioNotificaciones) {
this.servicioNotificaciones = servicioNotificaciones;
}
The lookup es:
public static Object getEjb(String lookupName){
Object t = null;
try {
Context ctx = new InitialContext();
t= ctx.lookup(lookupName);
} catch (NamingException e) {
log.error("getEjb | Error {}",e.getMessage(),e);
}
return t;
}
With those changes the mockito --> inject the mocks on the setter.

Categories