How can we unit test private class ?
For example with an example of private class #Autowired with qualifier
I would like to verify if the good qualifier is call
public class MyClass {
#Autowired
IHelloService helloService;
public void sayHello(List<Person> list) {
for(Person person : list) {
helloService.sayHello(person);
}
}
}
.
#Primary
#Component
public class SayHelloService implements ISayHello {
#Autowired
#Qualifier("french")
ISayHello french;
#Autowired
#Qualifier("english")
ISayHello english;
#Override
public void sayHello(Person person) {
switch (person.getLanguage) {
case "EN":
english.sayHello(Person person);
break;
case "FR":
french.sayHello(Person person);
break;
default:
break;
}
}
}
.
#Qualifier("french")
Component
class SayHelloFrenchService implements ISayHello {
public void sayHello(Person person) {
sysout("Bonjour " + person.getName());
}
}
#Qualifier("english")
Component
class SayHelloFrenchService implements ISayHello {
public void sayHello(Person person) {
sysout("Hello " + person.getName());
}
}
Edit:
I failed my example: the twice qualifier class were private
If i #Mock the interface it works.
I thought i must #Mock the implementation...
But i can't write tests of implementation of private class.
Related
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.
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";
}
}
I need to get the bean from the factory by name.
I wonder if there is a more elegant way to deal with this problem?
My working code now looks like this. This is my interface service and "factory"
public interface GreetingService {
void getGreeting(String name);
}
public interface GreetingServiceFactory {
GreetingService getGreetingService(String region);
}
Implementation greetingService:
#Service
public class EnglishGreetingServiceImpl implements GreetingService {
#Override
public void getGreeting(String name) {
System.out.println("Hello " + name);
}
}
#Service
public class GermanGreetingServiceImpl implements GreetingService {
#Override
public void getGreeting(String name) {
System.out.println("Willkommen " + name);
}
}
Implementation factory:
#Service
public class GreetingServiceFactoryImpl implements GreetingServiceFactory {
private Map<String, GreetingService> greetingBeanMap;
#Autowired
#Qualifier("germanGreetingServiceImpl")
private GreetingService germanGreetingService;
#Autowired
#Qualifier("englishGreetingServiceImpl")
private GreetingService englishGreetingService;
#PostConstruct
public void init () {
greetingBeanMap = new HashMap<>();
greetingBeanMap.put("en", englishGreetingService);
greetingBeanMap.put("de", germanGreetingService);
}
#Override
public GreetingService getGreetingService(String region) {
return greetingBeanMap.get(region);
}
}
Main class with example code where I receive bean after some name
#SpringBootApplication
public class SpringFactoryApplication implements CommandLineRunner {
#Autowired
private GreetingServiceFactory greetingServiceFactory;
public static void main(String[] args) {
SpringApplication.run(SpringFactoryApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
String config1 = "en";
GreetingService english = greetingServiceFactory.getGreetingService(config1);
english.getGreeting("John");
String config2 = "de";
GreetingService deutsch = greetingServiceFactory.getGreetingService(config2);
deutsch.getGreeting("Hans");
}
}
In your above code, this piece of code is completely redundant
#Autowired
#Qualifier("germanGreetingServiceImpl")
private GreetingService germanGreetingService;
#Autowired
#Qualifier("englishGreetingServiceImpl")
private GreetingService englishGreetingService;
#PostConstruct
public void init () {
greetingBeanMap = new HashMap<>();
greetingBeanMap.put("en", englishGreetingService);
greetingBeanMap.put("de", germanGreetingService);
}
this piece of code can be replaced by
#Autowired
private Map<String, GreetingService> greetingBeanMap;
When you declare like this, spring will search for all implementations of GreetingService interface and inject into your map, with the key as the bean name. i.e. the greetingBeanMap will have key's as germanGreetingServiceImpl and englishGreetingServiceImpl and value as the bean's itself.
If you want to make the key's as "en" and "de" instead of bean names, then you can name the beans as "en" and "de". Like this
#Service("en")
public class EnglishGreetingServiceImpl implements GreetingService {
......
}
#Service("de")
public class GermanGreetingServiceImpl implements GreetingService {
......
}
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.
let imagine I have per entity a repository class (spring data jpa) for database access and a service class. The dependencies are managed by spring framework. Every service method does in most cases the same, so there is mainly code duplication:
public class NewsService {
#Inject
private NewsRepository newsRepository;
public void add(News news) {
// do some validation
newsRepository.save(news);
}
}
public class UserService {
#Inject
private UserRepository userRepository;
public void add(User user) {
// do some validation
userRepository.save(user);
}
}
Now i thought about creating an abstract class like this:
public abstract class AbstractService<T> {
private UnknownRepository unknownRepository;
public void add(T entity) {
// do some validation
unknownRepository.save(entity);
}
}
public class NewsService extends AbstractService<News> {
}
public class UserService extends AbstractService<User> {
}
My problem: How can i overwrite the repository used inside the abstract class based on my entities?
You can replace the UnknownRepository field with an abstract method and a type parameter:
// R is the type of the repository
public abstract class AbstractService<T,R extends BaseRepository> {
protected abstract R getRepository();
public void add(T entity) {
getRepository().save(entity);
}
}
And inject the specific repository to the implementations of this class:
public class NewsService extends AbstractService<News, NewsRepository> {
#Inject private NewsRepository newsRepository;
#Override
public NewsRepository getRepository() {
return newsRepository;
}
// the inherited add() method works now
}