How to mock several static classes by PowerMockito - java

As we know PowerMockito can prepare for test only one final, static, or private class like following:
#RunWith(PowerMockRunner.class)
#PrepareForTest(SomeClassNumber1.class)
public class SomeClassUnderTest{
#Before
public void setUp() throws Exception {
}
#Test
public void testSomeMethod() throws Exception {
}
}
But the method I am going to test uses more than one static classes. The thing I want to do looks like:
#RunWith(PowerMockRunner.class)
#PrepareForTest(SomeClassNumber1.class, SomeClassNumber2.class)
public class SomeClassUnderTest{
But #PrepareForTest has only one argument.
Edit: For instance the method will use singleton, factory or some static methods of different classes.
public class SomeClass {
private Session session;
private Object object;
public void someMethod(int userId) {
Session session = Session.getSession(userId);
Object object = Singleton.getInstance();
}
}
Note: The problem could be solved by using single responsibility principle. e.g. instead of using singleton inside the method that is going to be tested, I can extract it to another method like following, and mock it:
public class SomeClass {
private Session session;
private Object object;
public void someMethod(int userId) {
session = getSession(userId);
object = getInstance();
}
private getSession(userId) {
return Session.getSession(userId);
}
private Object getInstance(){
return Singleton.getInstance();
}
}
But this too doesn't seem good solution for me. It is better if PowerMockito or other testing tools have the feature that can mock several static classes at the same time.

Edit: For instance the method will use singleton, factory or some static methods of different classes.
You should not use static access to class members and methods in the first place, you should also avoid the Java singelton pattern, There are other possibilities to make sure you have only one instance of a class at runtime.
Having written this lets answer your question:
#PrepareForTest has only one argument.
This single argument may be an array:
#PrepareForTest({Class1.class,Class2.class})

Related

How to mock a global object in a class properly?

Let say I have a class like so
Class A{
private K Obj = (K)(AppContext.getSpringContext().getBean("obj"))
public void method1{
// uses Obj
}
public void method2{
// uses Obj
}
}
And I need to write junits for method1 and method2 by changing the behavior of Obj in method 1 and method 2. In my junit class I am setting the appcontext as so:
public class AccountInformationManagerTest {
private CCBSAppContext appContext;
#Mock
ApplicationContext springContext;
#Mock
Object obj;
#Before
public void setup() throws Exception {
appContext = new CCBSAppContext();
appContext.setApplicationContext(springContext);
Mockito.when(springContext.getBean("obj")).thenReturn(obj);
}
}
As you can see, I am setting globally the appcontext. I don't want mock the static calls as I am using jacoco and powermockito doesn't integrate very well with jacoco. The problem here is that appcontext is now a global object which is shared across all methods and I need to modify the behavior of obj as I test the two methods. This will create a concurrency issue. How can I resolve this?
TBH avoid (K)(AppContext.getSpringContext().getBean("obj")) directly inject dependency or autowire the dependency . Then in its easily testable and add obj to the class via SpringTestUtils or setters.
#Autowired
K obj;
Else you can mock springContext getBean method for every test
#Test
void fun {
Mockito.when(springContext.getBean(Mockito.eq("obj"))).thenReturn(obj);
// doSomething
}
#Test
void fun2 {
Mockito.when(springContext.getBean(Mockito.eq("obj"))).thenReturn(obj2);
// doSomething2
}
Use reflection to set the Obj. Then you can set Obj to be a mocked object.
Here is a SO answer describing how to do it.

How can I test if a method was called on an object created inside the method to be tested

Is it possible to test that the "innerMethod" was called without modifying the Class class?
I need to make a unit test in a separate class both scenario of the "someCondition".
The problem is that the method is void so I cannot make use of the return type. The only way would be to check if the "innerMethod" was called.
I was thinking to use Mokito verify but this method is called inside a method on an object created at runtime.
Any suggestion is most welcome.
public class Class {
public void outerMethod(outerObj) {
if(someCondition) {
Object innerObj = new Object();
innerObj.innerMethod(outerObj);
} else {
//other code
}
}
You can achieve that with the use of Mockito::times and Mockito::verify methods.
test setup would be as follows:
#InjectMocks
private SomeService service;
#Mock
private SomeHelper helper;
and then test that some method from the helper has been involved in the following manner:
#Test
public void testInnerHasBeenCalledOnce() throws Exception {
service.outherMethodName(someParam);
Mockito.verify(helper, Mockito.times(1)).innerMethodName(someParamSecond);
}

PowerMockito mocking static class INSIDE enum?

I have a enum that has to have a inner static class for bean injection.
I feel I'm facing the most difficult situation for a mock: enum, static class, static field, static method..
public enum Category{
C1(Something(Constants.getFactory().createSomething(""))),
C2(...);
public static Constants {
#Autowired
private static Factory factory;
public static Factory getFactory(){
return factory;
}
}
}
And my testing class using PowerMockito is:
#RunWith(PowerMockRunner.class)
#PrepareForTest({Category.class,Category.Constants.class})
public class CategoryTests {
#Before
public void setUp() throws Exception {
PowerMockito.mockStatic(Category.class);
PowerMockito.mockStatic(Category.Constants.class);
//This simply testing mock didn't work
//PowerMockito.when(Category.Constants
// .getFactory()).thenReturn("123");
//I tried to mock the inner field 'factory' and use it directly without a getter
//(with small changes in the original class)
//But it didn't work either
Factory factory = PowerMockito.mock(Factory.class);
NewClass newClass = PowerMockito.mock(NewClass.class);
PowerMockito.when(Factory.createSomething(anySring()))
.thenReturn(newClass);
Whitebox.setInternalState(
Category.Constants.class,"factory",Factory);
//This is like the most common way to stub
//It didn't work, so I believe the inner static class were never mocked
PowerMockito.doReturn(factory).when(Category.Constants.class,
"getFactory", anyString());
}
//I don't know if real test cases matter that much but I update to add it for reference.
#Test(dataProvider = "Case1")
public void testFromFilterType(final String testName, String input, final Category expected) {
assertEquals(Category.doSomething(input), expected);
}
#DataProvider(name = "Case1")
Object[][] fromFilterTypeCases() {
return new Object[][] {
{ "C1", "input1", Category.C1 },
{ "C2", "input2", Category.C2 },
};
}
}
//Currently the tests got skipped because in class Category Constants.getFactory().createSomething(""),
//where Constants.getFactory() returns null and mocking doesn't work.
At first I didn't mock the Enum, but just the static inner class. After heavily searching, I tried in all ways. The setup seems correct but it may miss some tricks. Any helps?
A bit of guessing: Category.class is the class you intend to test. That class itself contains nothing that should require mocking/preparation.
So: drop these parts in your code. Even if it doesn't cause your current issue, I am pretty sure that it might have all kinds of unwanted consequences when you start testing things later on.
Beyond that, the real answer would be to avoid the necessity for PowerMock(ito) in the very first place. You are already using #Autowired, which implies that you are using a DI framework. Most DI frameworks also have hooks for unit testing. So you should rather try to get #Autowired to work in your test setup.

How to include private contructor in Line Coverage?

I am using jmockit to mock my classes for unit test purpose. Everything is working fine so far.
I have a factory which is thread safe and singleton as shown below:
So for below class, I am able to get 50% Line Coverage because I am not able to cover private constructor TestFactory().
public class TestFactory {
// not able to cover this
private TestFactory() {}
private static class TestHolder {
private static final TestClient INSTANCE = new TestClient();
}
public static IClient getInstance() {
return TestHolder.INSTANCE;
}
}
My question is - Is there any way I can cover TestFactory() private constructor so that I can get 100% Line Coverage in my Cobertura Report for this class?
Invoke it using reflection or just mockit.Deencapsulation.newInstance(). Write a test method like this
#Test
public void privateConstructorCoverage() throws Exception {
Deencapsulation.newInstance(TestFactory.class);
}
Deencapsulation javadoc
Provides utility methods that enable access to (ie "de-encapsulate") otherwise non-accessible fields, methods and constructors belonging to code under test.

How to Invoke a private method on a singleton (Enum style) in java?

Is there a way to invoke a private static method of an enumerated singleton? For example, say I have some legacy code that I need to test which has the following structure:
public enum Singleton {
INSTANCE;
private static double methodToTest() {
return 1.0;
}
public static String extremelyComplexMethod() {
methodToTest();
//Imagine lots more complex code here
return "";
}
}
How would I go about creating a class that tests methodToTest in isolation? I've tried reflection using Whitebox (included in PowerMock) but I've had no luck. Is there a way to do it?
I know that testing a private method directly is not the prefered method of doing things, but I want to know if its possible to test the private method directly. I tried to get the code to get recognized as java, but I was unsuccessful.
I was able to invoke the method try this following code
#RunWith(PowerMockRunner.class)
#PrepareForTest(Singleton.class)
public class MySingletonTest {
#Test
public void test() throws Exception{
Whitebox.invokeMethod(Singleton.class, "methodToTest");
}
}
Don't forget to add the Singleton class in PrepareForTest

Categories

Resources