I am using PowerMockito to mock a static method to get a better line coverage. The test passes but Cobertura still shows the return line as red. I am not sure if I am using PowerMockito in a wrong way. Any clues?
MUT
public static Object getBean( String beanName ) {
return AppContext.getApplicationContext().getBean( beanName );
}
Junit test
#RunWith ( PowerMockRunner.class )
#PrepareForTest ( {AppContext.class} )
private ApplicationContext applicationContext;
#Test
public void testGetBean() throws Exception {
String beanName = "junitBean";
applicationContext = Mockito.mock(ApplicationContext.class);
PowerMockito.mockStatic(AppContext.class);
AppContext.setApplicationContext(applicationContext);
PowerMockito.when(AppContext.getApplicationContext()).thenReturn(applicationContext);
PowerMockito.when(AppContext.getApplicationContext().getBean(beanName)).thenReturn(Object.class);
AppContext.getBean(beanName);
}
To mock static methods, PowerMock has to modify the Java Byte Code of your class after it has been compiled. Code coverage tools work in the same way - they "instrument" (google for "bytecode instrumentation") the bytecode in order to collect information about which lines of code have been run.
Given that there are two things here modifying already compiled code according to their own needs, it's not surprising that they don't work well together. In fact, other coverage tools have the same issue e.g. this issue on the PowerMock GitHub page.
It is precisely reasons like this that mocking static methods is a bad idea and should only really be done in very exceptional circumstances. In most cases, you can either wrap the static method call in an interface and pass an instance of the interface to your object under test, or better yet (if the code is under your control) remove the static method and replace it with an instance method.
You could also just pass the ApplicationContext instance directly into the constructor of your class under test.
Related
I want to unit test Java code that calls System.exit(-1) and want it to just do nothing instead of exiting the process. The underlying reason is that otherwise JaCoCo does not work properly and project guidelines want to see that line covered. Changing the tested code is not an option, too. Other calls to System should work normally. PowerMockito 2.0.7 is already used in the project and should be used here, too. My current Java version is 1.8.0_181 on Windows.
I tried with
PowerMockito.spy(System.class);
PowerMockito.doNothing().when(System.class, "exit", ArgumentMatchers.any(int.class));
//here comes the code under test that calls System.exit
It does not seem to work, System.exit seems to exit the process anyway.
How do it get this to work?
I think you should replace both the lines in your sample code
PowerMockito.spy(System.class);
PowerMockito.doNothing.....
to
PowerMockito.mockStatic(System.class);
This change works in my local as System.exit does nothing because of the mock on static method.
Also, I hope you are using PrepareForTest annotation
#PrepareForTest(CLASS_UNDER_TEST)
The spy method is to call real methods and have some wrapper around the non-static methods. Since you need a mock for static methods, mockStatic method should be used instead.
Update 1
The PowerMockito mockStatic method by default creates mock for all the static methods within the class. I don't have any clean solution. But, I can suggest a solution which looks ugly but does what is needed i.e only mock specific static method and remaining methods are invoking real methods. PoweMockito's mockStatic method is internally calling DefaultMockCreator to mock the static methods.
#RunWith(PowerMockRunner.class)
public class StaticTest {
#Test
public void testMethod() throws Exception {
// Get static methods for which mock is needed
Method exitMethod = System.class.getMethod("exit", int.class);
Method[] methodsToMock = new Method[] {exitMethod};
// Create mock for only those static methods
DefaultMockCreator.mock(System.class, true, false, null, null, methodsToMock);
System.exit(-1); // This will be mocked
System.out.println(System.currentTimeMillis()); // This will call up real methods
}
}
As per the PowerMockito documentation, the right way to call static void method is -
PowerMockito.mockStatic(SomeClass.class);
PowerMockito.doNothing().when(SomeClass.class);
SomeClass.someVoidMethod();
Reference - https://github.com/powermock/powermock/wiki/Mockito#how-to-stub-void-static-method-to-throw-exception
This should create the mock behaviour for the specific static void method. Unfortunately, this doesn't work for System Class because System class is final. Had it been not final, this would have worked. I tried it and I got this exception -
org.mockito.exceptions.base.MockitoException:
Cannot mock/spy class java.lang.System
Mockito cannot mock/spy because :
- final class
Code -
#Test
public void testMethod() throws Exception {
PowerMockito.mockStatic(System.class);
PowerMockito.doNothing().when(System.class);
System.exit(-1); // mockito error coming here
System.exit(-1);
System.currentTimeMillis();
}
my problem is that my unit test are slow because I'm publishing in a topic in those unit test, I would like to mock or change its behavior in some way. I was thinking in use reflection for this class and change the method behavior but I'm not sure if that is possible.
This is the behavior that I like to mock or change:
TopicCall.builder()
.toTopic(XXXX)
.withAttribute(XXXXXX, XXXXX)
.withAttribute(XXXXX, XXXXXX)
.withAttribute(XXXXX,XXXXX)
.publish();
I would like to do this because publis() is a real invocation and the test is slow and causing some problems in jenkins, because several unit test are publishing at the same time.
The Topic class is a public class with a static builder method which return a class instance, just like the next one:
public static TopicCall builder() {
return new TopicCall();
}
My problem is that I just acceding the method of this class from outside and I'm not sending the class in the constructor as example and I'm not able to mock its behavior, I'm not able to modify the TopicCall class because it is a .class utility from a jar, besides that I'm not able to use PowerMockito or another library, just Mockito, is there any way to achieve that?
Thanks!
Disclaimer: I missed the fact that PowerMock is forbidden for the author, but the answer could be useful for other users with the same problem.
PowerMock
As far as you want to mock a static method, then Mockito is not the solution.
This could be done using PowerMock.
PowerMock uses ClassLoader way for mocking, which could significantly increase tests time to run.
Here is an examle on Baeldung how to mock static methods.
Solution scratch:
#RunWith(PowerMockRunner.class)
#PrepareForTest({ TopicCall.class })
public class Test {
#Test
void test() {
mockStatic(TopicCall.class);
when(TopicCall.builder()).thenReturn(/*value to be returned*/ null);
// the test code...
}
}
I've been testing my code behavior using TestNG and JMockit for a while now and I have had no specific issue with their combination. Today I came across a situation where I needed to mock one of my internal dependencies, in the so called, type wide manner and I did not need to keep that mock around since none of the test cases dealt with it directly while they counted on the mocked version functionality. So, naturally, I put the mocking logic in my #BeforeMethod. Here is a sample:
public class SampleTest
{
#Mocked
#Cascading
private InnerDependency dependency;
#BeforeMethod
public void beforeMethod()
{
new NonStrictExpectations()
{
{
dependency.getOutputStream((String)any);
result = new Delegate<OutputStream>()
{
public OutputStream getOutputStream(String url)
{
return null;
}
};
}
};
}
#Test
public void testNormalOperation()
{
// The test whose desired behavior depends on dependency being mocked out
// ..
}
}
But, since my tests do not care about the mocked dependency explicitly, I'm not willing to declare it as a test class field, unlike what is done above. To my knowledge of JMockit The only options remaining would be:
Declare dependency as a local mock field:
new NonStrictExpectations()
{
#Cascading
private InnerDependency dependency;
{
//...
}
}
Declare dependency as an input argument for beforeMethod(), similar to what is done for normal #Test methods:
#BeforeMethod
public void beforeMethod(#Mocked #Cascading final InnerDependency dependency)
{
// ...
}
I see that JMockit 1.6+ would not like the first option and warns with WARNING: Local mock field "dependency" should be moved to the test class or converted to a parameter of the test method. Hence, to keep everyone happy, I'm ruling this option out.
But for the second option, TestNG (currently 6.8.6) throws exception when running the test saying java.lang.IllegalArgumentException: wrong number of arguments. I don't see this behavior with normal #Test cases passed with #Mocked parameters. Even playing with #Parameter and #Optional will not help (and should not have!).
So, is there any way I could make this work without declaring the unneccessary test class mock field, or am I missing something here?
Thanks
Only test methods (annotated with #Test in JUnit or TestNG) support mock parameters, so the only choice here is to declare a mock field at the test class level.
Even if not used in any test method, I think it's better than having it declared in a setup method (using #Before, #BeforeMethod, etc.). If it were to be possible, the mock would still have to apply to all tests, because of the nature of setup methods; having a mock field of the test class makes it clear what the scope of the mock is.
Dynamic partial mocking is one more technique to specify #Mocked dependencies locally. However, it has it's limitations (see comments below).
I have a folder path set in system variable through JVM arguments in Eclipse and I am trying to access it in my class as:
System.getProperty("my_files_path").
While writing junit test method for this class, I tried mocking this call as test classes do not consider JVM arguments. I have used PowerMockito to mock static System class and tried returning some path when System.getProperpty is being called.
Had #RunWith(PowerMockRunner.class) and #PrepareForTest(System.class) annotations at class level. However, System class is not getting mocked as a result I always get null result.
Any help is appreciated.
Thanks Satish. This works except with a small modification. I wrote PrepareForTest(PathFinder.class), preparing the class I am testing for test cases instead of System.class
Also, as mock works only once, I called my method right after mocking.
My code just for reference:
#RunWith(PowerMockRunner.class)
#PrepareForTest(PathInformation.class)
public class PathInformationTest {
private PathFinder pathFinder = new PathFinder();
#Test
public void testValidHTMLFilePath() {
PowerMockito.mockStatic(System.class);
PowerMockito.when(System.getProperty("my_files_path")).thenReturn("abc");
assertEquals("abc",pathFinder.getHtmlFolderPath());
}
}
There are certain classes PowerMock can't mock in the usual way. See here:
https://code.google.com/p/powermock/wiki/MockSystem
This, however, may still not work. In order of "good design" preference, you can fall back to these:
Refactor your code! Using a System property for passing a file path around is probably not the best way. Why not use a properties file loaded into a Properties object? Why not use getters/setters for the components that need to know this path? There are many better ways to do this.
The only reason I could think of not to do this is you're trying to wrap a test harness around code you "can't" modify.
Use #Before and #After methods to set the System property to some known value for the test(s). You could even make it part of the #Test method itself. This will be FAR easier than attempting to mock through PowerMock. Just call System.setProperty("my_files_path","fake_path");
System class is declared as final and cannot be mocked by libraries such as PowerMock. Several answers posted here are incorrect. If you are using Apache System Utils you can use getEnvironmentVariable method instead of calling System.getenv directly. SystemUtils can be mocked since it is not declared as final.
Set the system property in your test and ensure that it is restored after the test by using the rule RestoreSystemProperties of the library System Rules.
public class PathInformationTest {
private PathFinder pathFinder = new PathFinder();
#Rule
public TestRule restoreSystemProperties = new RestoreSystemProperties();
#Test
public void testValidHTMLFilePath() {
System.setProperty("my_files_path", "abc");
assertEquals("abc",pathFinder.getHtmlFolderPath());
}
}
The System.setter or getter method should be put in a user defined method and that method can be mocked to return the desired property in unit test.
public String getSysEnv(){
return System.getEnv("thisprp");
}
#RunWith(PowerMockRunner.class)
#PrepareForTest(System.class)
public class MySuperClassTest {
#Test
public void test(){
PowerMockito.mockStatic(System.class);
PowerMockito.when(System.getProperty("java.home")).thenReturn("abc");
System.out.println(System.getProperty("java.home"));
}
}
Sailaja add System.class because as per the power mock guidelines for static,private mocking you should add the class in prepare for test.
#PrepareForTest({PathInformation.class,System.class})
Hope this helps.let me know if it doesn't work
I'm trying to implement unit testing for aproject, it uses a legacy "utility" project that is littered with static methods and many of the classes are final or their methods are final. I'm not able to update the legacy project at all.
JMock and EasyMock both choke on final methods, and I don't see a nice way to test the static calls. What techniques are there to test these?
If you're able to refactor your code, you can wrap your calls to the final/static methods in simple instance methods, for example:
protected Foo doBar(String name) {
return Utility.doBar(name);
}
This allows you to override your wrapper method in the unit test to return a mock instance of Foo.
Alternatively you can use Powermock, which extends Easymock (and Mockito) to allow mocking of final and static methods:
PowerMock is a framework that extend other mock libraries such as EasyMock with more powerful capabilities. PowerMock uses a custom classloader and bytecode manipulation to enable mocking of static methods, constructors, final classes and methods, private methods, removal of static initializers and more.
Here's an example test mocking a static final method, the example shows how to mock some other types too:
#Test
public void testMockStaticFinal() throws Exception {
mockStatic(StaticService.class);
String expected = "Hello altered World";
expect(StaticService.sayFinal("hello")).andReturn("Hello altered World");
replay(StaticService.class);
String actual = StaticService.sayFinal("hello");
verify(StaticService.class);
assertEquals("Expected and actual did not match", expected, actual);
// Singleton still be mocked by now.
try {
StaticService.sayFinal("world");
fail("Should throw AssertionError!");
} catch (AssertionError e) {
assertEquals("\n Unexpected method call sayFinal(\"world\"):",
e.getMessage());
}
}
How about a level of indirection / Dependency Injection?
Since the legacy utility project is your dependency, create an interface to separate it out from your code. Now your real/production implementation of this interface delegates to the legacy utility methods.
public LegacyActions : ILegacyActions
{
public void SomeMethod() { // delegates to final/static legacy utility method }
}
For your tests, you can create a mock of this interface and avoid interacting with the legacy utility thingie.
JMockit allows you to mock static methods and final classes. I assume it uses some classloadin-fu, although I haven't really looked into it.
JMockit Expectations API allows
expectations to be set on any kind of
method invocation (on interfaces,
abstract classes, concrete final or
non final classes, and on static
methods), as well as on class
instantiation through any
constructors.
As already pointed out, JMockit can be used.
An example:
#Test
public void mockStaticAndFinalMethods(#Mocked LegacyService mock) {
new Expectations() {{
LegacyService.staticMethod("hello"); result = "Hello altered World";
}};
String actual = LegacyService.staticMethod("hello");
new LegacyService().finalMethod(123, "test");
assertEquals("Hello altered World", actual);
new Verifications() {{
mock.finalMethod(123, "test"); // verify this call occurred at least once
}};
}
If your non-refactorable method uses something like JNDI to connect to another service, I'd consider starting a JDNI service and populating it with stubs which you control. It's a pain but relatively straightforward. It may mean setting up a database or JMS listener or whatever but there should be a lightweight java implementation you can drop into the tests.
JMock together with JDave can mock final methods and classes, if you need to. Here
are instructions. That being said I would treat this legacy code (as others have suggested already) as an external dependency and build interfaces and mock those. It is another layer of indirection, but since you can't change that legacy code, it seems to be a reasonable one.