What I have right now
I have a 3rd party singleton instance that my class under test relies on and that singleton is using System.getenv(String) in its constructor. Is it possible to mock this call?
I tried this
JMockIt Example
new Expectations()
{
System mockedSystem;
{
System.getenv( "FISSK_CONFIG_HOME" ); returns( "." );
}
};
But it gives me an EXCEPTION_ACCESS_VIOLATION and crashes the JVM.
Is there another way to set a system environment variable for a unit test?
In this case you need to use partial mocking so that JMockit doesn't redefine everything in the System class. The following test will pass:
#Test
public void mockSystemGetenvMethod()
{
new Expectations()
{
#Mocked("getenv") System mockedSystem;
{
System.getenv("envVar"); returns(".");
}
};
assertEquals(".", System.getenv("envVar"));
}
I will soon implement an enhancement so that issues like this don't occur when mocking JRE classes. It should be available in release 0.992 or 0.993.
PowerMock seams to be able to mock system classes.
Your other option (assuming you are not unit testing the 3rd party API) is to create a for Facade the 3rd party API that has a nice, easy mockable interface and have your test classes use this rather than the real thing.
Oh, JMockIt supports this too:
package playtest;
import static org.junit.Assert.*;
import mockit.*;
import mockit.integration.junit4.JMockit;
import org.junit.*;
import org.junit.runner.RunWith;
#RunWith(JMockit.class)
public class JMockItTest {
#Test
public void mockSystemGetEnv() {
Mockit.setUpMocks(MockSystem.class);
assertEquals("Bye", System.getenv("Hello"));
}
#MockClass(realClass = System.class)
public static class MockSystem {
#Mock
public static String getenv(String str) {
return "Bye";
}
}
}
You can't change the environment but you can change the access to it: Simply wrap the call to System.getenv() in a method or a helper class and then mock that.
[EDIT] Now your problem is how to change the code of your third party library. The solution here is to use a Java decompiler and to fix the class. If you want, you can send in a feature request in, too. Add that new class to your test suite. That should make your IDE find the class for the tests.
Since test code doesn't go into production, you can run your tests and the production code will use the original library.
A while back I wanted to test System.exit, and found a solution by using a custom SecurityManager. You can verify the call is being made, and the argument of the call, but using this method, you can't mock the return value of the call.
An update on #Rogério answer.
In my case with JMockit 1.25 I had to do it using the MockUp API:
#Test
public void mockSystemGetenvMethod(){
new MockUp<System>()
{
#Mock
public String getenv(final String string) {
return "";
}
};
assertEquals(".", System.getenv("envVar"));
}
Related
Is there a way to mock a Repository without the #RunWith(MockitoJUnitRunner) annotation on the class?
I have a test that passed without the annotation but fails with it. Without it, my repo test doesn't work. It's a catch 22.
When I use that annotation, my when() methods in my tests no longer stub behavior, mocks do nothing, and despite setting break ppoints and those breakpoints being hit (indicating the line/method is run), verify(..., times(x)) statements say the mocked object never interacted with that method. I've been pulling my hair out on why using the #RunWith(MockitoJUnitRunner) annotation would make the most simple parts of Mockito not work.
I can't find any threads asking about this but maybe someone knows better keywords to use. Does this sound like a known issue?
Here is my test:
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import static org.mockito.MockitoAnnotations.initMocks;
// toggling this below annotation is the source of grief.
//#RunWith(MockitoJUnitRunner.class)
public class LoadEditEntityChangeLogServiceImplTest {
#InjectMocks
private ServiceImpl serviceMock;
#Mock
private EditStepRepository editStepRepository;
#Mock
private EditMapper editMapper;
#Before
public void init() {
initMocks(this);
}
#Test // when the RunWith is commented out, this passes. When it is not, the test fails the verify assert.
public void mapEditEntityFromAction_Test() {
EditDTO editDTO = Mockito.mock(EditDTO.class);
when(editDTO.getSysNum()).thenReturn((long)7334);
EditEntity editEntity = new editEntity();
editEntity.setSysNum(editDTO.getSysNum());
when(editMapper.mapToEntity(eq(editDTO))).thenReturn(editEntity);
editEntity response = serviceMock.mapEditEntityFromAction(editDTO);
verify(loadEditMapper, times(1)).mapToEntity(eq(loadEventDTO));
assertEquals(loadEventDTO.getSystemNumber(), response.getSystemNumber());
}
#Test // this will fail without the #RunWith as the mocked repo will be null and throws NullPointerException when used.
public void updateConvertedEventSegment_Test() {
EditEntity editEntity = new EditEntity();
EditStepEntity editStepEntity = new EditStepEntity();
editEntity.setEditStep(editStepEntity);
doReturn(editStepEntity).when(editStepRepository).save(any());
serviceMock.updateEditStep(editEntity);
verify(editEntity, times(1)).getEditStep();
verify(editStepRepository, times(1)).save(eq(editStepEntity));
}
}
You should understand what does this runner actually do:
Basically it allows injecting mocks (prepared by mockito with Mockito.mock(...) ) into the test fields annotated with #Mock. In the question, since you've commented out the runner, all these fields will be null.
When you annotated something with #InjectMocks - it will inject the mocks into the fields of the object of type of the annotated reference.
One more point to clarify here: MockitoAnnotations.initMocks(this) will do the same as the "runner" so no need to include both (you should use initMocks if you can't use the runner for some reason, like if there is already another runner that must be used)
Now, you ask:
Is there a way to mock a Repository without the #RunWith(MockitoJUnitRunner) annotation on the class?
The answer is - yes, you can, in fact you don't have to use the runner, sometimes its more convenient.
So, assuming you really use this runner, the real question is what exactly do you mean by "my repository doesn't work". Does this mean that there exists a reference in the service that points of this repository and its null?
Does it mean that there is a mock of repository but when you execute the call "under the test" the mock is different?
You don't show it in the code, but I assume you have some like this:
public class ServiceImpl {
private final EditStepRepository editStepRepository;
public ServiceImpl(EditStepRepository editStepRepository) {
this.editStepRepository = editStepRepository;
}
...
}
But if so, once you create a mock (and indeed there should be a mock injected into the ServiceImpl class (check this out with debugger or something), There should be expectatations specified on the repository, usually there should be code like this in the test:
Mockito.when(editStepRepository.doSomething(...)).thenReturn(...)
You haven't placed any of these lines, that why it doesn't work.
But all-in-all since the question contains many uncertain technicalities like this, I can't tell more than that other that speculating...
a question:
When I do something like:
package path.to.common.package.test;
#BeforeClass
public class CommonTestSetup {
public void setUp() {
// Setup Stiff
}
}
And the other class setup in the same package:
package path.to.common.package.test;
public class TestTest extends CommonTestSetup {
#Test
public void testGetTestReturnsCorrectStrings() {
// do asserts etc
}
}
And then executing JUnit test on testGetTestReturnsCorrectStrings I am getting an error:
org.junit.runners.model.InvalidTestClassError: Invalid test class 'org.junit.runner.manipulation.Filter':
1. No runnable methods
at org.junit.runners.ParentRunner.validate(ParentRunner.java:456)
at org.junit.runners.ParentRunner.<init>(ParentRunner.java:99)
at org.junit.runners.BlockJUnit4ClassRunner.<init>(BlockJUnit4ClassRunner.java:84)
at org.junit.runners.JUnit4.<init>(JUnit4.java:23)
at org.junit.internal.builders.JUnit4Builder.runnerForClass(JUnit4Builder.java:10)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:66)
at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:37)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:66)
at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:39)
at org.junit.internal.requests.FilterRequest.getRunner(FilterRequest.java:36)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.createFilteredTest(JUnit4TestLoader.java:80)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.createTest(JUnit4TestLoader.java:71)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.loadTests(JUnit4TestLoader.java:46)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:523)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:761)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:461)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:207)
Why is that? Does it mean JUnit 4 does not support Test classes extending a base class?
The idea behind this was to create a single SetUp base class, shared by many other test classes that need it.
Originally I even tried to have the base class in another package entirely, then moved it to the same package for testing, and got a different error (the one above).
use import org.junit.Test;
instead of import org.junit.jupiter.api.Test;
Based on the JUnit documentation:
Annotating a public static void no-arg method with #BeforeClass causes it to be run once before any of the test methods in the class
So move the #BeforeClass annotation to the setUp method and make into a static method.
You may also check for things like
class classname {
class another class{
}
}
Keep one class in file it helps
I experienced similar ... I hopes it help someone ;-)
I have the following simple code. I have a class (TestClass) and I want to test "someMethod". There is an external static method which is called by my "someMethod".
I want to Powermock that static method to return me some dummy object.
I have the #PrepareForTest(ExternalClass.class) in the begining, but when I execute it gives the error:
The class ExternalClass not prepared for test.
To prepare this class, add class to the '#PrepareForTest' annotation.
In case if you don't use this annotation, add the annotation on class or method level.
Please help me to point out what is wrong with the way I have used #PrepareForTest
#RunWith(PowerMockRunner.class)
#PrepareForTest(ExternalClass.class)
public class xyzTest {
#Mock
private RestTemplate restTemplate;
#Mock
private TestClass testClass;
#BeforeClass
private void setUpBeforeClass() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testSuccessCase() {
Boolean mockResponse = true;
ResponseEntity<Boolean> response = new ResponseEntity<Boolean>(mockResponse, HttpStatus.OK);
SomeClass someClass = new SomeClass("test", "1.0.0", "someUrl", "someMetaData");
PowerMockito.mockStatic(ExternalClass.class);
Mockito.when(restTemplate.postForEntity(any(String.class), any(String.class), eq(Boolean.class))).thenReturn(response);
Mockito.when(ExternalClass.getSomeClass(any(String.class))).thenReturn(someClass);
Boolean result = testClass.someMethod("test");
Assert.isTrue(result);
Mockito.verify(restTemplate, times(1)).postForObject(any(String.class), any(String.class), any());
}
}
Make sure you add #RunWith(PowerMockRunner.class) to the top of your class as well.
::edit:: two years later...
Don't ever use PowerMockito, you shouldn't need to.
If you do need to, you have most likely broken the SOLID principles and your design is wrong.
Fix your design instead.
As with the last answer, my problem was also mixing the Test annotation from TestNG instead of Junit Test.
import org.junit.Test; // works
import org.testng.annotations.Test // did not work
Very abstruse error and I spent more than 5 hrs debugging :(
For those trying to get this working with Junit 5, If your using the powermock-module-junit4 beta release which claims to be compatible with 4+, the library will still not recognize:
import org.junit.jupiter.api.Test;
and it will throw a:
org.powermock.api.mockito.ClassNotPreparedException
when #PrepareForTest is applied on the class you want to static mock. If you want to use PowerMock, you will have to go back to Junit 4 or create a MockWrapper for your static method at this time.
PowerMock 2.0: Github Roadmap
While the top-rated answer here is correct without a doubt, this does not answer the question of why is that needed; or, for example, why the same thing would not work with adding #RunWith(MockitoJUnitRunner.class).
The thing is PowerMockRunner uses instrumentation API under the hood, via
javassist library, this allows to alter the classes, like remove final or mock static (non-compile time constants).
In the process of modifying (instrumenting) a certain class, they add an interface to that, called PowerMockModified. It is a marker interface that denotes that a certain byte-code instrumentation took place. Later in the code, they simply check if the class that you use in #PrepareForTest was actually instrumented in some way or not, via such a method:
private boolean isModifiedByPowerMock() {
return PowerMockModified.class.isAssignableFrom(this.type);
}
In turns out that PowerMockRunner does some instrumentation, while MockitoJUnitRunner does not; thus the error you get.
I had the same error, resolved this by adding
#Rule
public PowerMockRule rule = new PowerMockRule();
inside the test class.
If above answers don't work try extends PowerMockTestCase. This trick worked for me.
Example:
public class xyzTest extends PowerMockTestCase
check if import org.junit.Test; package has imported and not that api jupiter one.
I had the same error but resolved it. My problem was that I included powermock-module-junit4 but included my test annotation from TestNG instead of Junit.
I had the same error. I was using TestNG to run the tests. I had to use the following method to fix the above issue.
#ObjectFactory
public IObjectFactory getObjectFactory() {
return new PowerMockObjectFactory();
}
For testNG there are 2 options as follows :
Using ObjectFactory as below:
#ObjectFactory
public IObjectFactory getObjectFactory() {
return new PowerMockObjectFactory();
}
Test class extending extends org.powermock.modules.testng.PowerMockTestCase
My gradle was using Junit 5.
test {
useJUnitPlatform()
}
I was able to debug this. By having breakpoints in PowerMockRunner methods.
It was not invoked. Moreover JUnit 5 is not supported with PowerMockito.
Looks like JUnit5 runs without #ExtendWith.
Make sure you are using powermock2. I had this problem when I was using powermock.
Use
import org.powermock2.api.mockito.PowerMockito;
I am trying to just run a simple test case. I have the following method.
public static void run(String[] args) throws Throwable {
CommandLineArguments opts = CommandLineOptionProcessor.getOpts(args);
}
I will continue to build this method / test case as I go. However I just wanted to make sure a simple test case worked first. So I wrote the following test.
#Test
public void testRun() {
String[] args = {"--arg1", "value", "--arg2", "value2"};
mockStatic(CommandLineOptionProcessor.class);
expect(CommandLineOptionProcessor.getOpts(args));
EasyMock.replay(CommandLineOptionProcessor.class);
}
After that I get the following error:
java.lang.IllegalStateException: no last call on a mock available
I read some of the other posts on StackOverflow but their solution seemed to be that they were using PowerMock with Mockito. I am using Powermock and Easymock, so that should not be the problem.
I followed Rene's advice and added the following to the top of my class.
#PrepareForTest(CommandLineOptionProcessor.class)
#RunWith(PowerMockRunner.class)
public class DataAssemblerTest {
I fixed the previous error. But now I have this error.
java.lang.IllegalArgumentException: Not a mock: java.lang.Class
at org.easymock.internal.ClassExtensionHelper.getControl(ClassExtensionHelper.java:61)
at org.easymock.EasyMock.getControl(EasyMock.java:2172)
at org.easymock.EasyMock.replay(EasyMock.java:2074)
.
.
.
Any ideas on what could be causing this would be great.
Did you annotate the test class with #RunWith(PowerMockRunner.class) and #PrepareForTest(CommandLineOptionProcessor.class)?
#RunWith(PowerMockRunner.class)
#PrepareForTest(CommandLineOptionProcessor.class)
public class TestClass {
#Test
public void testRun(){
You need the #PrepareForTest(CommandLineOptionProcessor.class) at the test class level. See the Powermock doc:
Use the #PrepareForTest(ClassThatContainsStaticMethod.class) annotation at the class-level of the test case.
Also ensure that the required libraries are on the test classpath.
In your case the javassist library is missing. Put it on the classpath. Maybe some other libs are also missing... we will see.
If you get
java.lang.IllegalArgumentException: Not a mock: java.lang.Class
then you are using EasyMock.replay(), but you must use PowerMock.replay()
EasyMock.expectLastCall()
or
EasyMock.expectLastCall().anyTimes()
or
EasyMock.expectLastCall().andAnswer(..)
is not present in your code, must be after the method you want to test
this is in case your test method is a void method.
otherwise you can use :
expect(CommandLineOptionProcessor.getOpts(args)).andReturn(object);
also please add this to you test class :
#ObjectFactory
public IObjectFactory getObjectFactory() {
return new org.powermock.modules.testng.PowerMockObjectFactory( );
}
For some reason I fail to follow a pretty straight forward PowerMock example.
I included powermock-mockito-1.5.1-full in my classpath, and I try to test a public final method (following this example).
For some reason I am not able to make the import to the PowerMock class.
import org.junit.*;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import com.cleancode.lifesaver.camera.*;
#RunWith(PowerMockRunner.class)
#PrepareForTest(android.hardware.Camera.class)
public class CameraTests {
private android.hardware.Camera _cameraMock;
#Before
public void setUp() {
_cameraMock = PowerMockito.mock(android.hardware.Camera.class);
}
#Test
public void releaseCamera() {
ICamera camera = new Camera(_cameraMock);
// Compile error: PowerMock can't be resolved
PowerMock.replay(_cameraMock);
// I also tried PowerMockito.replay(_cameraMock) but that also doesn't exist.
camera.release();
Mockito.verify(_cameraMock).release();
}
}
As the comment explains, the PowerMock class can't be imported from the power mock jar.
It feels like a silly question, but I really can't find anything on the internet.
Where should I be able to find the static class PowerMock? I also used Java Decompile to search the powermock library, no hits on powermock / replay.
The example you are following PowerMock.replay(_cameraMock); is using EasyMock, while you seem to be wanting Mockito. Take a look at this tutorial for mockito & power mock
I suggest you not to create your mock in your setUp() (Before) method, because a mock is very complicated, for example you can tell it exactly how many time it should expect a method is called, if you declare a "general" mock for all your tests it's very difficult to control this behaviour.
maybe (without the code I can only guess) you want that your android.hardware.Camera is called inside your Camera.release() method, am I right? so I whould do like this:
The method you are trying to mock is not static, it's a normal final method. You can try to do this:
android.hardware.Camera mock = PowerMock.createMock(android.hardware.Camera.class);
PowerMock.expect(mock.release());
PowerMock.replay();
ICamera camera = new Camera(mock);
camera.release();
PowerMock.verify(mock);
if inside camera.relase() is not called exactly once the android.hardware.Camera.release() method the test fails.