Powermockito whenNew returns null if not matched - java

I do not know if it supposed to do that, but I guess not. Have a look to my code below.
File mocked = PowerMockito.mock(File.class);
PowerMockito.whenNew(File.class).withParameterTypes(String.class).withArguments(eq(THE_TARGET_PATH)).thenReturn(mocked);
File normalFile = new File(WORKING_PATH);
File mockedFile = new File(THE_TARGET_PATH);
I do expect that the normalFile will be created normally, but it is actually null. The mockedFile is mocked correctly btw.
I am also using #RunWith(PowerMockRunner.class) and #PrepareForTest({ClassWhereInstanceIsCreated.class, File.class})
and I am using:
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.7.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.7.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-core</artifactId>
<version>1.7.4</version>
<scope>test</scope>
</dependency>

My finding shows that there is no good way to do partial constructor mocking with PowerMockito / PowerMockito 2. By the logic you should be able to do something like
PowerMockito.whenNew(File.class).withParameterTypes(String.class)
.withArguments(eq(WORKING_PATH)).thenCallRealMethod();
But this will trigger internal exception in the PowerMockito similar to this
org.mockito.exceptions.base.MockitoException: Cannot call abstract real method on java object! Calling real methods is only possible when mocking non abstract method. //correct example: when(mockOfConcreteClass.nonAbstractMethod()).thenCallRealMethod();
Thus, the only way I can see is to re-write the test. You should construct all your required File objects first, before mocking constructor and give PowerMockito rules which to return in each particualr case.
File mocked = Mockito.mock(File.class);
// create file as you want
File realFile = new File(WORKING_PATH);
// tell PowerMockito to return it
PowerMockito.whenNew(File.class).withParameterTypes(String.class)
.withArguments(Mockito.eq(WORKING_PATH)).thenReturn(realFile);
// tell PowerMockito to return mock if other argument passed
PowerMockito.whenNew(File.class).withParameterTypes(String.class)
.withArguments(Mockito.eq(THE_TARGET_PATH)).thenReturn(mocked);
File normalFile = new File(WORKING_PATH);
File mockedFile = new File(THE_TARGET_PATH);
This is undesirable solution, but I could not offer anything better.
Hope it helps!

Related

class junit.framework.TestSuite cannot be cast to class org.junit.jupiter.api.Test

I am new to JAVA and Junit and trying to do something simple. I have the test passed but I see in the terminal initailizationError side this error "class junit.framework.TestSuite cannot be cast to class org.junit.jupiter.api.Test" This is the version of my Junit dependencies.
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.9.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.9.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
This is the test that I am trying to run
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Assertions;
/**
* Unit test for simple App.
*/
public class AppTest
extends TestCase
{
#Test
public void firstTest() {
Assertions.assertEquals(2, 2);
}
/**
* Create the test case
*
* #param testName name of the test case
*/
/**
* #return the suite of tests being tested
*/
public static Test suite()
{
return (Test) new TestSuite( AppTest.class );
}
/**
* Rigourous Test :-)
*/
public void testApp()
{
assertTrue( true );
}
}
I could not understand the error as it is my first time useing it
I think the simplest fix is for you to delete your suite method. You don't need it, and your tests will run quite happily without it. Once you are a bit more confident and familiar with Java and JUnit, then maybe suites will help you organise and group tests, but you can certainly start without them.
The way you are attempting to create a test suite seems to follow the approach of JUnit 3, but you are using JUnit 5. JUnit changed a lot from JUnit 3 and JUnit 4, and also from JUnit 4 to JUnit 5, so it's not surprising that something from JUnit 3 doesn't work with JUnit 5.
I hadn't seen this way of writing test suites before, but I did find that this page talks about JUnit 5 and then presents examples using JUnit 3. To be quite frank I found the content of that page to be of poor quality and cannot recommend it. If you are using that page to learn about JUnit then I would advise you to look elsewhere.
the issue comes from these lines :
public static Test suite()
{
return (Test) new TestSuite( AppTest.class );
}
you are trying to cast 2 different Classes :
class junit.framework.TestSuite
to
class org.junit.jupiter.api.Test
which are 2 different classes , you can simply remove this function and the error will be fixed.
or if you want to have a TestSuite to bundle a few unit test cases and run them together,it can be in a separate class.
like what described in this article
hope this helps !!!

Cannot resolve method 'getLifecycle' in 'Allure'

I have project in java and i am using Allure for generating a test report.
I found this question and I need to rename my test using the below code:
AllureLifecycle lifecycle = Allure.getLifecycle();
//change test name to "CHANGED_NAME"
lifecycle.updateTestCase(testResult -> testResult.setName("CHANGED_NAME"));
I have imported the AllureLifecycle from below:
import io.qameta.allure.AllureLifecycle;
and using the below dependency:
<dependency>
<groupId>io.qameta.allure</groupId>
<artifactId>allure-java-commons</artifactId>
<version>2.13.3</version>
</dependency>
But, in the line Allure.getLifecycle() it is complaining with Cannot resolve method 'getLifecycle' in 'Allure'.
How can fix the error?
You need to add import of io.qameta.allure.Allure class

Mockito Mock a static void method with Mockito.mockStatic()

I'm using Spring Boot and in one of my unit test, I need to mock the Files.delete(somePath) function. Which is a static void method.
I know that with Mockito it is possible to mock void method:
doNothing().when(MyClass.class).myVoidMethod()
And since July 10th 2020, it is possible to mock static method:
try (MockedStatic<MyStaticClass> mockedStaticClass = Mockito.mockStatic(MyStaticClass.class)) {
mockedStaticClass.when(MyStaticClass::giveMeANumber).thenReturn(1L);
assertThat(MyStaticClass.giveMeANumber()).isEqualTo(1L);
}
But I can't manage to mock a static void mehtod such as Files.delete(somePath).
This is my pom.xml file (only test related dependencies):
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>3.5.15</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.5.15</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>3.5.15</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<version>2.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
Is there a way to mock static void methods without using PowerMockito ?
If it is possible, what is the correct syntax to do so ?
In general mocking static calls is the last resort, that is not supposed to be used as default approach.
For example, for testing the code, that works with file system, there are better means. E.g. depending on the junit version either use TemporaryFolder rule or #TempDir annotation.
Also, please note, that Mockito.mockStatic might significantly slow down your tests (e.g. look at the notes below).
Having said the caution above, find the snippet below, that shows how to test, that file got removed.
class FileRemover {
public static void deleteFile(Path filePath) throws IOException {
Files.delete(filePath);
}
}
class FileRemoverTest {
#TempDir
Path directory;
#Test
void fileIsRemovedWithTemporaryDirectory() throws IOException {
Path fileToDelete = directory.resolve("fileToDelete");
Files.createFile(fileToDelete);
FileRemover.deleteFile(fileToDelete);
assertFalse(Files.exists(fileToDelete));
}
#Test
void fileIsRemovedWithMockStatic() throws IOException {
Path fileToDelete = Paths.get("fileToDelete");
try (MockedStatic<Files> removerMock = Mockito.mockStatic(Files.class)) {
removerMock.when(() -> Files.delete(fileToDelete)).thenAnswer((Answer<Void>) invocation -> null);
// alternatively
// removerMock.when(() -> Files.delete(fileToDelete)).thenAnswer(Answers.RETURNS_DEFAULTS);
FileRemover.deleteFile(fileToDelete);
removerMock.verify(() -> Files.delete(fileToDelete));
}
}
}
Notes:
Mockito.mockStatic is available in Mockito 3.4 and above, so check you're using correct version.
The snippet deliberatly shows two approaches: #TempDir and Mockito.mockStatic. When run both tests you'll notice that Mockito.mockStatic is much slower. E.g. on my system test with Mockito.mockStatic runs around 900 msec vs 10 msec for #TempDir.

JUnit change class or method behaviour in integration tests

I have a java application (no Spring inside) that I want to test with an integration test.
My main use case is the main function that with a specified input do some things on the database and send some request to two different services, one SOAP and one REST.
Now I have a working JUnit configuration (splitted in unit and integration tests) + io.fabric8:docker-maven-plugin that use a docker image for the database during integration tests.
What I'm trying to do is to add a mock for these 2 services, in particular, the method that is used to call directly the external service.
The big problem is that I have this structure:
class A{
Result mainFunction(Request r){
....
B b = new B(params);
b.logEvent(someParameters)
....
}
}
class B{
int logEvent(Object someParameters){
....
NotifierHandler nh = new NotifierHandler(param1);
nh.sendNotification(json);
....
}
}
where I have:
class NotifierHandler{
String sendNotification(Json j){
...
[call to REST service with some parameters]
...
...
[call to SOAP service with some parameters]
...
}
}
What I need: call A.mainFunction(r) having, in the test environment, replaced the NotifierHandler with a FakeNotifierHandler and/or change the behaviour of the method sendNotification().
Actual problems: Using Mockito and PowerMock now I have the problem that I'm not able to change globally and directly the class NotifierHandler with FakeNotifierHandler. The same trying to changing the behaviour of the method.
In particular, what I need is to create a
class FakeNotifierHandler{
String sendNotification(Json j){
...
[save on an HashMap what I should send to the REST service]
...
...
[save on another HashMap what I should send to the SOAP service]
...
}
}
Reading all example that I tryed I saw only simple examples that change the return value of a method and not the behaviour of one method of one class used by another and another that I'm using as the start point of the integration test.
NOTE: probably there is a fast way to do this but I'm very new on this type of tests (Mockito, PowerMock,...) and I have found no example for this particular strange case.
EDIT: not similar to How to mock constructor with PowerMockito because I need to change the behaviour of the method, not only the return value.
Thanks a lot in advance
I found a solution that works very well and it is very simple!
The solution is PowerMock (https://github.com/powermock/powermock) and in particular replace the creation of an instance of a class with another: https://github.com/powermock/powermock/wiki/mockito#how-to-mock-construction-of-new-objects
There is only one problem in my project and it is JUnit 5. PowerMock support JUnit 4 and for this reason, only for some tests of the solution are using it.
In order to do this there is the needed to replace
import org.junit.jupiter.api.Test;
with
import org.junit.Test;
In order to use teh "whenNew()" methods I had extented the class that in tests must be replaced and I have overwritten only methods that are necessary for the integration test.
The big benefit of this solution is that my code is untouched and I can use this approach also on old code without the risk of introducing regressions during the refactor of the code.
Regarding the code of a integration test, here an example:
import org.junit.jupiter.api.DisplayName;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
#RunWith(PowerMockRunner.class)
#PowerMockIgnore({"javax.crypto.*" }) // https://github.com/powermock/powermock/issues/294
#PrepareForTest(LegacyCoreNetworkClassPlg.class) // it is the class that contains the "new SOAPCallHelper(..)" code that I want to intercept and replace with a stub
public class ITestExample extends InitTestSuite {
#Test
#DisplayName("Test the update of a document status")
public void iTestStubLegacyNetworkCall() throws Exception {
// I'm using JUnit 4
// I need to call #BeforeAll defined in InitTestSuite.init();
// that works only with JUnit 5
init();
LOG.debug("IN stubbing...");
SOAPCallHelperStub stub = new SOAPCallHelperStub("empty");
PowerMockito.whenNew(SOAPCallHelper.class).withAnyArguments().thenReturn(stub);
LOG.debug("OUT stubbing!!!");
LOG.debug("IN iTestStubLegacyNetworkCall");
...
// Here I can create any instance of every class, but when an instance of
// LegacyCoreNetworkClassPlg.class is created directly or indirectly, PowerMock
// is checking it and when LegacyCoreNetworkClassPlg.class will create a new
// instance of SOAPCallHelper it will change it with the
// SOAPCallHelperStub instance.
...
LOG.debug("OUT iTestStubLegacyNetworkCall");
}
}
Here the configuration of the pom.xml
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.jupiter.version>5.5.2</junit.jupiter.version>
<junit.vintage.version>5.5.2</junit.vintage.version>
<junit.platform.version>1.3.2</junit.platform.version>
<junit.platform.engine.version>1.5.2</junit.platform.engine.version>
<powermock.version>2.0.2</powermock.version>
<!-- FOR TEST -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<!-- Only required to run tests in an IDE that bundles an older version -->
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>${junit.platform.version}</version>
<scope>test</scope>
</dependency>
<!-- Only required to run tests in an IDE that bundles an older version -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<!-- Only required to run tests in an IDE that bundles an older version -->
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>${junit.vintage.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-engine</artifactId>
<version>${junit.platform.engine.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit.vintage.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
I think the main headache in your case is that you have tightly coupled dependencies between class A, B and NotifierHandler. I would start with:
class A {
private B b;
public A(B b) {
this.b = b;
}
Result mainFunction(Request r){
....
b.logEvent(someParameters)
....
}
}
class B {
private NotifierHandler nh;
public B(NotifierHandler nh) {
this.nh = nh;
}
int logEvent(Object someParameters){
....
nh.sendNotification(json);
....
}
}
Make NotifierHanlder an interface:
interface NotifierHandler {
String sendNotification(String json);
}
and make two implementations: one for a real use case, and one fake that you can stub whatever you want:
class FakeNotifierHandler implements NotifierHandler {
#Override
public String sendNotification(String json) {
// whatever is needed for you
}
}
Inject FakeNotifierHandler in your test.
I hope this helps you.

How to mock riak java client?

I'm trying to unit test code that uses com.basho.riak:riak-client:2.0.0. I mocked all riak client classes and was hoping to get a useless but working test. However, this fails with a null pointer:
java.lang.NullPointerException
at com.basho.riak.client.api.commands.kv.KvResponseBase.convertValues(KvResponseBase.java:243)
at com.basho.riak.client.api.commands.kv.KvResponseBase.getValue(KvResponseBase.java:150)
at com.basho.riak.client.api.commands.kv.FetchValue$Response.getValue(FetchValue.java:171)
My test looks like this:
#Test public void test() {
RiakClient riakClient = mock(RiakClient.class);
#SuppressWarnings("unchecked")
RiakCommand<FetchValue.Response, Location> riakCommand = (RiakCommand<FetchValue.Response, Location>) mock(RiakCommand.class);
Response response = mock(Response.class);
when(riakClient.execute(riakCommand)).thenReturn(response);
Response returnedResponse = riakClient.execute(riakCommand);
when(response.getValue(Object.class)).thenReturn(new Object());
MyPojo myData = returnedResponse.getValue(MyPojo.class);
// Make assertions
}
How do you unit test code that uses the riak client? Eventually I would like to ensure that the expected type/bucket/key combination is used and that the expected RiakCommand is run.
EDIT: I dug more into the FetchValue class and found this structure:
FetchValue
- is public final
FetchValue.Response
- is public static,
- has a package-private constructor Response(Init<?> builder)
FetchValue.Response.Init<T> is:
- protected static abstract class Init<T extends Init<T>> extends KvResponseBase.Init<T>
And there is FetchValue.Response.Builder:
static class Builder extends Init<Builder>
- with build() that: return new Response(this);
I assume that Mockito gets lost somewhere among the inner classes and my call ends up in KvResponseBase.convertValues, where the NP is thrown. KvResponseBase.convertValues assumes a List<RiakObject> of values and I see no sane way of assigning it.
I have investigate a bit your case. I have reduce your example to this simple SSCCE:
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import org.junit.Test;
import com.basho.riak.client.api.commands.kv.FetchValue.Response;
public class RiakTest {
#Test
public void test() throws Exception {
Response response = mock(Response.class);
given(response.getValue(Object.class)).willReturn(new Object());
}
}
which throws this error:
java.lang.NullPointerException
at com.basho.riak.client.api.commands.kv.KvResponseBase.convertValues(KvResponseBase.java:243)
at com.basho.riak.client.api.commands.kv.KvResponseBase.getValue(KvResponseBase.java:150)
at com.basho.riak.client.api.commands.kv.FetchValue$Response.getValue(FetchValue.java:171)
at RiakTest.test(RiakTest.java:12)
After some digging, i think i have identified the problem. It is that you are trying to stub a public method which is inherited from a package (visibility) class:
abstract class KvResponseBase {
public <T> T getValue(Class<T> clazz) {
}
}
It seems that Mockito fails to stub this method so the real one is invoked and a NullPointerException is thrown (due to an access of a null member: values).
One important thing to note is that if this function invocation not fails, Mockito would show a proper error:
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);
Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods.
Those methods *cannot* be stubbed/verified.
Mocking methods declared on non-public parent classes is not supported.
2. inside when() you don't call method on mock but on some other object.
I guess it is a Mockito bug or limitation so i have open an issue in the Mockito tracker where i have reproduce your case with simple classes.
UPDATE
The issue i opened is in fact a duplicate of an existing one. This issue will not be fixed but a workaround exists. You may use the Bytebuddy mockmaker instead of the cglib one. Explanations could be found here.
You can not mock final classes and final and/or static methods with mockito. Note that static nested classes are fine. This is because mockito subclasses (I'm not 100% sure this is the exact operation, it uses CGLIB to generate classes) objects, but isn't allowed to override final methods or extend the final classes. For static methods no overriding is ever possible.
In your code you probably are trying to call a final class or method. It is hard to tell which class causes the problem, from your NullPointer stackstrace you should suspect the first object on it that you have mocked (going up starting from the testcase method). The method on the mock should not be calling any other methods (expect internal to mockito), so probably that is final, because you is does not seem to call a 'mocked' method.
In your case the stacktrace is not complete (as your testcase is not on it). In a quick look on the riak framework I couldn't find the method take a look at FetchValue$Response.getValue.
Also note the following. From the snippet you posted, I can not tell what you are testing in your testcase. All objects you create are mocks. Normally you have 1 (or a few) real classes that you are testing. The other classes (that interact with your classes under test) you mock, to be able to simulate complex behavior.
Follow up:
Thanks #gontard I was able to find this:
<dependency>
<!-- We need this fix: https://github.com/mockito/mockito/pull/171 to use mockito with Riak -->
<!--http://stackoverflow.com/questions/28442495/how-to-mock-riak-java-client#28474106-->
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.0.52-beta</version>
<scope>test</scope>
</dependency>
That has the fixes included.
Unfortunately, if you use both Fetch and MultiFetch (likely), you're up a creek.
MultiFetch.Response is a final class (so you can use mockito, you need to use PowerMock)
FetchValue.Response has the issues you outlined, and can only be fixed with the beta mockito, not available with powermock yet...
Update, I figured out how to use both mockito & powermock together (until powermock upgrades):
<!-- We need this to mock Multi-Fetch responses from Riak, which are final -->
<!-- However, we need the beta version of mockito due to bugs (see below),
so we _cannot_ use the mockito api provided by powermock, do _not_ include _powermock-api-mockito, it'll mess stuff up -->
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.6.4</version>
<scope>test</scope>
</dependency>
<!--If we don't include this, we get: -->
<!--java.lang.IllegalStateException:
Extension API internal error: org.powermock.api.extension.proxyframework.ProxyFrameworkImpl could not be located in classpath.-->
<!-- it looks like this is due to some discrepancy in packaging with mockito 2, this may be fixed in Fall 2016:
https://groups.google.com/forum/#!topic/powermock/cE4T40Xa_wc -->
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-easymock</artifactId>
<version>1.6.4</version>
</dependency>
<!-- We need this fix: https://github.com/mockito/mockito/pull/171 to use mockito with Riak -->
<!--http://stackoverflow.com/questions/28442495/how-to-mock-riak-java-client#28474106-->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.0.52-beta</version>
<scope>test</scope>
</dependency>

Categories