PowerMock mockStatic not mocking the class - java

I have the following class(and method in it)
class Fetcher{
public void fetch(String key){
File file = File.createTempFile(key,"*.txt");
.....
....
}
}
I want to unit test this method and want to mock the createTempFile method
For this i have written the unit test as follows
#RunWith(PowerMockRunner.class)
#PrepareForTest({File.class})
public class FetcherTest {
public void test() {
String key = "key";
File file = new File("Hello");
PowerMock.mockStatic(File.class);
EasyMock.expect(File.createTempFile(EasyMock.anyObject(String.class),EasyMock.anyObject(String.class))).andReturn(file).once();
PowerMock.replay(File.class);
Fetcher fetcher = new Fetcher();
fetcher.fetch("key");
PowerMock.verify(File.class);
}
}
Executing the unit test provides the following error:
Expectation failure on verify: File.createTempFile(,):
expected: 1,actual: 0
I have looked through a lot of articles but am not able to figure out what's missing here and why File is not getting mocked. Please help with any suggestions

When you mock Java System classes (and the File is a Java System Class) you have to add a ClassThatCallsTheSystemClass to #PrepareForTest.
So you need to add class Fetcher to #PrepareForTest
#RunWith(PowerMockRunner.class)
#PrepareForTest({Fetcher.class})

Related

Mockito mocks locally final class but fails in Jenkins

I have written some unit tests for a static method. The static method takes only one argument. The argument's type is a final class. In terms of code:
public class Utility {
public static Optional<String> getName(Customer customer) {
// method's body.
}
}
public final class Customer {
// class definition
}
So for the Utility class I have created a test class UtilityTests in which I have written tests for this method, getName. The unit testing framework is TestNG and the mocking library that is used is Mockito. So a typical test has the following structure:
public class UtilityTests {
#Test
public void getNameTest() {
// Arrange
Customer customerMock = Mockito.mock(Customer.class);
Mockito.when(...).thenReturn(...);
// Act
Optional<String> name = Utility.getName(customerMock);
// Assert
Assert.assertTrue(...);
}
}
What is the problem ?
Whereas the tests run successfully locally, inside IntelliJ, they fail on Jenkins (when I push my code in the remote branch, a build is triggered and unit tests run at the end). The error message is sth like the following:
org.mockito.exceptions.base.MockitoException: Cannot mock/spy class
com.packagename.Customer Mockito
cannot mock/spy because :
- final class
What I tried ?
I searched a bit, in order to find a solution but I didn't make it. I note here that I am not allowed to change the fact that Customer is a final class. In addition to this, I would like if possible to not change it's design at all (e.g. creating an interface, that would hold the methods that I want to mock and state that the Customer class implements that interface, as correctly Jose pointed out in his comment). The thing that I tried is the second option mentioned at mockito-final. Despite the fact that this fixed the problem, it brake some other unit tests :(, that cannot be fixed in none apparent way.
Questions
So here are the two questions I have:
How that is possible in the first place ? Shouldn't the test fail both locally and in Jenkins ?
How this can be fixed based in the constraints I mentioned above ?
Thanks in advance for any help.
An alternative approach would be to use the 'method to class' pattern.
Move the methods out of the customer class into another class/classes, say CustomerSomething eg/CustomerFinances (or whatever it's responsibility is).
Add a constructor to Customer.
Now you don't need to mock Customer, just the CustomerSomething class! You may not need to mock that either if it has no external dependencies.
Here's a good blog on the topic: https://simpleprogrammer.com/back-to-basics-mock-eliminating-patterns/
How that is possible in the first place? Shouldn't the test fail both locally and in Jenkins ?
It's obviously a kind of env-specifics. The only question is - how to determine the cause of difference.
I'd suggest you to check org.mockito.internal.util.MockUtil#typeMockabilityOf method and compare, what mockMaker is actually used in both environments and why.
If mockMaker is the same - compare loaded classes IDE-Client vs Jenkins-Client - do they have any difference on the time of test execution.
How this can be fixed based in the constraints I mentioned above?
The following code is written in assumption of OpenJDK 12 and Mockito 2.28.2, but I believe you can adjust it to any actually used version.
public class UtilityTest {
#Rule
public InlineMocksRule inlineMocksRule = new InlineMocksRule();
#Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
#Test
public void testFinalClass() {
// Given
String testName = "Ainz Ooal Gown";
Client client = Mockito.mock(Client.class);
Mockito.when(client.getName()).thenReturn(testName);
// When
String name = Utility.getName(client).orElseThrow();
// Then
assertEquals(testName, name);
}
static final class Client {
final String getName() {
return "text";
}
}
static final class Utility {
static Optional<String> getName(Client client) {
return Optional.ofNullable(client).map(Client::getName);
}
}
}
With a separate rule for inline mocks:
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.mockito.internal.configuration.plugins.Plugins;
import org.mockito.internal.util.MockUtil;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class InlineMocksRule implements TestRule {
private static Field MOCK_MAKER_FIELD;
static {
try {
MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(Field.class, MethodHandles.lookup());
VarHandle modifiers = lookup.findVarHandle(Field.class, "modifiers", int.class);
MOCK_MAKER_FIELD = MockUtil.class.getDeclaredField("mockMaker");
MOCK_MAKER_FIELD.setAccessible(true);
int mods = MOCK_MAKER_FIELD.getModifiers();
if (Modifier.isFinal(mods)) {
modifiers.set(MOCK_MAKER_FIELD, mods & ~Modifier.FINAL);
}
} catch (IllegalAccessException | NoSuchFieldException ex) {
throw new RuntimeException(ex);
}
}
#Override
public Statement apply(Statement base, Description description) {
return new Statement() {
#Override
public void evaluate() throws Throwable {
Object oldMaker = MOCK_MAKER_FIELD.get(null);
MOCK_MAKER_FIELD.set(null, Plugins.getPlugins().getInlineMockMaker());
try {
base.evaluate();
} finally {
MOCK_MAKER_FIELD.set(null, oldMaker);
}
}
};
}
}
Make sure you run the test with the same arguments. Check if your intellij run configurations match the jenkins. https://www.jetbrains.com/help/idea/creating-and-editing-run-debug-configurations.html. You can try to run test on local machine with the same arguments as on jenkins(from terminal), if it will fail that means the problem is in arguments

Added a junit test file to the test package in eclipse but it will not run

I am using JUnit4 in an Eclipse IDE. I have one test file with 7 tests that run fine by selecting Run As JUnit.
I added another file for another set of tests. I have 1 test in the file.
I believe I created the test correctly...
This the file / test
#RunWith(MockitoJUnitRunner.class)
public class CloseSummaryOrCloseTrailerResponseTest {
#InjectMocks
XMLTransaction xmlTransaction;
#Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
}
#Test
public void whenCloseSummaryResponseNoErrorExpectCorrectXmlMsgProduced ()
{
..code to run the test
}
}
When I select the file and chose 'Run As Junit' nothing happens.
What am I missing?
UPDATE
Sorry for the incomplete information...
The project tree is as follows:
src/java/...source files
src/test/com/javaserver/transaction/RequestTest.java
/com/javaserver/transaction/ResponseTest.java
I can run the RequestTest file and all tests pass.
When I try to run the ResponseTest file, there was no output initially.
I restarted Eclipse and when I run the response test, I get the error:
org.mockito.exceptions.base.MockitoException: Cannot instantiate
#InjectMocks field named 'xmlTransaction' of type 'class...
I imported the XMLTransaction class. Yet it cannot be instantiated.
I don't have a main method. I thought by adding #RunWith(MockitoJUnitRunner.class) it runs the class. It runs the other class file.
UPDATE
It looks like the object that I want to mock needs a 1 argument constructor.
The constructor would look like this:
XMLTransaction xmlTrans = new XMLTransaction(URL)
The URL is just a text string and not an actual URL.
But how do I instantiate the object?
If I put it under the #InjectMocks, I get the compile error:
Default constructor cannot handle exception type Exception thrown by
implicit super constructor. Must define an explicit constructor
UPDATE
I need to use a PropertyManager class in order to create the XMLTransaction class. I use this code under the #InjectMocks to do that:
String wlUrl = PropertyManager.getInstance().getProperty(URL);
But then I get the error:
Cannot handle Property exception.
UPDATE
I tried replacing #MockInjects to #Mock and used the init() to create the class:
public final static String URL="WL_APPSERVER";
#Mock
XMLTransaction xmlTransaction;
#Before
public void initMocks() throws Exception {
XMLTransaction xmlTransaction = new XMLTransaction(URL);
}
I get the error:
Mockito cannot mock this class: class
com.fedex.ground.tms.javaserver.dock.transaction.XMLTransaction.
Mockito can only mock non-private & non-final classes. Underlying
exception : java.lang.IllegalArgumentException: Could not create type
If I add a default constructor without an argument, I get the same error.
This is a public class and not final.
UPDATE
I solved the problem above by adding an instance of the PropertyManager.
This object needs a property file.
The error now is that it can't find the property file.
I have this now.
#RunWith(MockitoJUnitRunner.class)
public class CloseSummaryOrCloseTrailerResponseTest {
public final static String URL="WL_APPSERVER";
public final static String PROP_FILE = "src/config/tms20.properties";
#Mock
XMLTransaction xmlTransaction;
#Mock
PropertyManager propertyManager;
#Before
public void initMocks() throws Exception {
MockitoAnnotations.initMocks(this);
Properties p = new Properties();
p.load(new FileReader(new File(PROP_FILE)));
propertyManager.getInstance(PROP_FILE);
XMLTransaction xmlTransaction = new XMLTransaction(URL);
}
What do I need to identify the property file?
Place your Tests in hierarchy like this:
project
src
main
java // source files
resources // xml, properties etc
test
java // source files
resources // xml, properties etc
Code must be something like this:
#RunWith(MockitoJUnitRunner.class)
public class ExampleTest {
#Mock
private List list;
#Test
public void shouldDoSomething() {
list.add(100);
}
}
https://static.javadoc.io/org.mockito/mockito-core/2.6.8/org/mockito/junit/MockitoJUnitRunner.html
The problem was that the constructor was calling a private method to initialize the class. Since Mockito can't deal with private methods, I solved the problem by using PowerMock.

JUnit parameter in #Rule #Before and #After

I need an advice on how the following snippet can be implemented in all test methods.
#Test
public void testCatalogItemUpdate() {
String correlationId = getCorrelation();
try {
parallel().actions(
//use correlationId
//invoke test scenario
//assert results
);
} finally {
print(correlationId);
}
}
I read about #Rule and #Before and #After annotations.
I can not comment as i don't have enough points so i tried to give the solution with my understanding. So please clarify one thing that you added the comments with in parameter section like below
parallel().action(/*test scenario and assert results are here*/);
Any specific reason for it?
Here is the code snippet. Please check if it helps
private String correlationId;
#Before
public void beforeEachTest(){
correlationId = getCorrelation();
parallel().actions(correlationId);
}
#Test
public void testCatalogItemUpdate(){
//Execute test scenario using correlationId
//Assert results
}
#After
public void afterEachTest(){
print(correlationId);
}
If you need to perform those actions either at the beginning of test or at the end of it, you may try to use those BeforeTest or AfterTest components. I'm not sure if you can call Citrus tests within those sections, but you can try. Here is an example of usage of before test (you need to add it to citrus-context file):
<citrus:before-test id="defaultBeforeTest">
<citrus:actions>
<citrus-test:java class="<class path and class name to be here" >
<citrus-test:method name="<method name>" >
<citrus-test:argument>${<if any variable should be passed>}</citrus-test:argument>
<citrus-test:argument>1</citrus-test:argument>
</citrus-test:method>
</citrus-test:java>
<!-- access to Citrus method -->
<citrus-test:purge-channel>
<citrus-test:channel name="<server_name>.inbound"/>
</citrus-test:purge-channel>
</citrus:actions>
</citrus:before-test>
You may also look at the Templates in Citrus (http://www.citrusframework.org/reference/html/templates.html), especially if you need to run that code in the middle of your test case.
You can use the BeforeTestSupport classes in Citrus
You should extend the BeforeTestSupport classes and add these to the Spring application context:
public class MyBeforeTest extends TestDesignerBeforeTestSupport {
#Override
public void beforeTest(TestDesigner designer) {
designer.echo("This action should be executed before suite");
}
}
<bean id="myBeforeTest" class="my.company.citrus.MyBeforeTest"/>
The beforeTest method is provided with the test designer instance which is then capable to receive the Java DSL calls in Citrus. Of course you can also use the test runner alternative with respective base TestRunnerBeforeTestSupport class.

Pass parameters to JUnit test from the TestSuite class

I want to create 2 JUnit TestSuites. They both utilize the same test classes, but they should each use different parameters. For example, in test suite A, I want my data to be collected from file A and to be written to database A. In test suite B, I want my data to be collected from file B and to be written to databaseB.
The reason I use testSuites for this is because:
I can put all the specific parameters in the testsuite classes
I can reuse the testclasses
I can choose which testsuite to run. I do not want all tests to always run with all possible paramaters!
The problem is I cannot really pass the parameters. I understand the way the Parameterized class works with JUnit, but it does not allow point 3 in the list above. If I use the code below it will run my test class with both databse connections, which is not what I want to achieve.
#RunWith(value = Parameterized.class)
public class TestCheckData
{
private File file;
private DatabaseSource databaseSource;
public TestCheckData(File file, DatabaseSource databaseSource)
{
this.file = file;
this.databaseSource = databaseSource;
}
#Parameters
public static Iterable<Object[]> data1()
{
return Arrays.asList(new Object[][]
{
{ TestSuiteA.DATA_FILE_A, TestSuite1.DATABASE_A },
{ TestSuiteB.DATA_FILE_B, TestSuite1.DATABASE_B }
});
}
I already find some way of passing configurations in a spring context in this question, but I'm not using any special framework.
Well, this would be a little unconventional, but you could add a different Test class to the beginning of each suite run that would set the parameters you want to use for that test. So you'd have classes like:
public abstract class StaticParameters {
public static File dataFileToUse = null;
public static DatabaseSource databaseToUse = null;
}
public class Suite1Params extends StaticParameters {
#BeforeClass
public static void setParams() {
dataFileToUse = DATA_FILE_A;
databaseToUse = DATABASE_A;
}
}
public class Suite2Params extends StaticParameters {
#BeforeClass
public static void setParams() {
dataFileToUse = DATA_FILE_B;
databaseToUse = DATABASE_B;
}
}
Then you'd just make Suite1Params or Suite2Params the first in your suite list. You might have to add a fake #Test entry to the params classes, I'm not sure if the Suite runner requires that.
You could modify the tests so that they get the parameters from a config file. This way you would always only have 1 Suite.
The path of the config file can be looked up via a System property.
Then on the invocation of the test suite, you could pass in a different config file by changing the property using the -D option on the JVM.
So for example if you named the proprerty env.properties then your command would be:
%java -Denv.properties=prod.config runMyTests
or
%java -Denv.properties=dev.config runMyTests
etc

Mocking a class object using Mockito and PowerMockito

Is it possible to mock a class object using Mockito and/or PowerMockito?
Something like:
Class<Runnable> mockRunnableClass = mock(Class<Runnable>.class);
An alternative to mocking Class might be to use a Factory instead. I know you are concerned about refactoring, but this could be done without changing the public API of the class. You haven't provided much code to understand the class you are trying to test, but here's an example of refactoring without changing the API. It's a trivial class, but it might give you an idea.
public class Instantiator {
public Runnable getNewInstance(Class<Runnable> runnableClass) throws Exception {
return runnableClass.newInstance();
}
}
Of course, the easiest thing to do to test this trivial class would be to use a genuine Runnable class, but if you tried to mock the Class, you would run into the problems you're having. So, you could refactor it thus:
public class PassThruFactory {
public Object newInstance(Class<?> clazz) throws Exception {
return clazz.newInstance();
}
}
public class Instantiator {
private PassThruFactory factory = new PassThruFactory();
public Runnable getNewInstance(Class<Runnable> runnableClass) throws Exception {
return (Runnable)factory.newInstance(runnableClass);
}
}
Now Instantiator does exactly the (trivially simple) thing it was doing before with the same public API and no need for any client of the class to do any special injecting of their own. However, if you wanted to mock the factory class and inject it, that's very easy to do.
why not using an agent if you can't refactor the code there isn't many options, as #jherics mentionned, java system classes are loaded by the bootstrap classloader and powermock can't redefine their bytecode.
However Powermock now coms with an agent, that will allow system classes mock. Check here for complete explanation.
The main idea is to modify your java command and add :
-javaagent: path/to/powermock-module-javaagent-1.4.12.jar
The basic thing this agent is doing is to definalize classes, to allow future mocking in a specific test, that's why you'll need to use specific types to communicate with the agent, for example with JUnit :
#Rule PowerMockRule rule = new PowerMockRule(); // found in the junit4 rule agent jar
TestNG is also supported. Just check the wiki page for more information.
Hope that helps.
First, as stated in the comments, you would need to do:
Class<Runnable> mockRunnableaClass = (Class<Runnable>)mock(Class.class);
But that won't work in the usual way because of a limitation with PowerMock. You cannot simply mock classes in from java.lang, java.net, java.io or other system classes because they're loaded by Java's bootstrap classloader and cannot be byte-code manipulated by PowerMock's classloader. (See PowerMock FAQ #4.) As of PowerMock 1.2.5, you can work around this. If the class you wanted to test was this:
public class ClassToTest {
private Class<Runnable> runnableClass;
public void setRunnableClass(Class<Runnable> runnableClass) {
this.runnableClass = runnableClass;
}
public Runnable foo() {
return runnableClass.newInstance();
}
}
Then you would do this:
#RunWith(PowerMockRunner.class)
#PrepareForTest({ ClassToTest.class }) // Prepare the calling class for test
public class SystemClassUserTest {
#Test
public void testFoo() throws Exception {
Class<Runnable> mockClass = (Class<Runnable>) mock(Class.class);
Runnable mockRunnable = mock(Runnable.class);
ClassToTest objectUT = new ClassToTest();
objectUT.setRunnableClass(mockClass);
when(mockClass.newInstance()).thenReturn(mockRunnable);
assertThat(objectUT.foo(), is(sameInstance(mockRunnable);
}
}
How about this. creating a get method of the has a Object (MS) in class PCService and then mock it.
public class PCService implements PCServiceIf {
public MSIf getMS() {
return ms;
}
private MSIf ms = new MS();
public boolean isMovieAccessibleToMyLevel(String myLevel, String movieId) {
return getMS().getPCL(movieId);
}
}
#Test
public void testIsMovieAccessibleToMyLevelMock() {
msMock = mock(MS.class);
spy = spy(new PCService());
doReturn(msMock).when(spy).getMS();
when(msMock.getPCL(movieId)).thenReturn(value);
when(spy.getMS().getPCL(movieId)).thenReturn(value);
assertTrue(spy.isMovieAccessibleToMyLevel("PG", movieId) == true);
}

Categories

Resources