PowerMockito mock indirect static method - java

Class to test
public class Randomer {
public int get() {
return (int) Math.random() + 1;
}
}
The test class
package org.samiron;
import static org.junit.Assert.assertEquals;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.api.support.membermodification.MemberMatcher;
import org.powermock.api.support.membermodification.MemberModifier;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.testng.annotations.Test;
/**
* #author samiron
*
*/
#RunWith(PowerMockRunner.class)
#PrepareForTest({ Randomer.class, Math.class })
public class RandomerTest {
#Test
public void shouldAddUpDieRollsCorrectly() throws Exception {
PowerMockito.spy(Math.class);
MemberModifier.stub(MemberMatcher.method(Math.class, "random")).toReturn(2.0);
Randomer d = new Randomer();
assertEquals(3, d.get());
}
}
Always getting java.lang.AssertionError: expected:<3> but was:<1>
Whats going wrong here? To be honest, every time I came across a situation to mock a static function I try to find a way around instead of wasting time. So need your help to figure out the exact solution.
The sole purpose of the example classes is to demonstrate that Math.random() function is not mocked and therefore not returning the desired value.
General realization
Mocking is a every essential tool while writing tests. Although mocking on instances works quite as expected but mocking static methods seems to be a real complicated with so many combinations of mocking libraries and so many options to support just few simple scenarios. This should be streamlined.
Libraries used:
mockito-all-1.9.5.jar
powermock-mockito-release-full-1.5.1-full.jar

This test passes thereby proving that the static call Math.random() is successfully mocked:
#RunWith(PowerMockRunner.class)
// tell PowerMock about (a) the class you are going to test and (b) the class you are going to 'mock static'
#PrepareForTest({Randomer.class, Math.class })
public class RandomerTest {
#Test
public void shouldAddUpDieRollsCorrectly() throws Exception {
// prepare PowerMock for mocking statics on Math
PowerMockito.mockStatic(Math.class);
// establish an expectation for what Match.random() should return
PowerMockito.when(Math.random()).thenReturn(2.0);
Randomer d = new Randomer();
assertEquals(3, d.get());
}
}
The main differences between this and what you posted in your question are is that I am using ...
PowerMockito.mockStatic(Math.class) and PowerMockito.when(Math.random()).thenReturn(2.0)
... instead of:
PowerMockito.spy(Math.class) and MemberModifier.stub(MemberMatcher.method(Math.class, "random")).toReturn(2.0)
Also, in your OP the example code uses a mixture of JUnit (org.powermock.modules.junit4.PowerMockRunner) and TestNG (org.testng.annotations.Test) whereas in my example I am just using JUnit.
The above has been verified with
junit:4.12
powermock-module-junit4:1.7.0
powermock-api-mockito2:1.7.0

There seems to be a library mismatch here.
In the comments you stated to use the following dependencies (without the convenience of Maven):
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-mockito-release-full</artifactId>
<version>1.5.1</version>
<scope>test</scope>
</dependency>
I got your code working using these:
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.7.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.7.0</version>
<scope>test</scope>
</dependency>

Related

How to mock static method in Junit 5? [duplicate]

I want to mock a static method in JUnit 5. But unfortunately, JUnit 5 doesn’t support Mockito. Is there another method to achieve the same other than reverting back to JUnit 4?
From Mockito 3.4.0 (2020-07-10), it is possible to mock static methods out of the box even in JUnit 5, without any extension.
In the documentation, you can find an example: 48. Mocking static methods (since 3.4.0)
Important note: You need to use inline mock maker. So the dependency to use is not the core one:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>3.4.6</version>
<scope>test</scope>
</dependency>
Example:
Class under test:
package teststatics;
public class FooWithStatics {
public static Long noParameters() {
return System.currentTimeMillis();
}
public static String oneParameter(String param1) {
return param1.toUpperCase();
}
}
Test class:
package teststatics;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
public class FooWithStaticsTest {
#Test
void testStatic() {
// Before mock scope, usual behavior.
assertNotEquals(0L, FooWithStatics.noParameters());
assertNotEquals("yyy", FooWithStatics.oneParameter("xxx"));
// Mock scope
try (MockedStatic mocked = mockStatic(FooWithStatics.class)) {
// Mocking
mocked.when(FooWithStatics::noParameters).thenReturn(0L);
mocked.when(() -> FooWithStatics.oneParameter("xxx")).thenReturn("yyy");
// Mocked behavior
assertEquals(0L, FooWithStatics.noParameters());
assertEquals("yyy", FooWithStatics.oneParameter("xxx"));
// Verifying mocks.
mocked.verify(times(1), FooWithStatics::noParameters);
mocked.verify(times(1), () -> FooWithStatics.oneParameter("xxx"));
}
// After mock scope returns to usual behavior.
assertNotEquals(0L, FooWithStatics.noParameters());
assertNotEquals("yyy", FooWithStatics.oneParameter("xxx"));
}
}
The short answer is no, as the Mockito team is done with their work and is waiting for the JUnit team for an extension and are discussing here a lot.
With some overhead you can: As JUnit 5 provides support for running legacy JUnit 4, and there you can use Mockito. So you can create tests in Junit4 for these cases:
A sample project for migration setup with gradle and with mvn. From there I am using PowerMock 2.0 beta with Mockito 2.
The reason why Mockito doesn't provide static methods mocking at the moment is because of the common belief that static method shouldn't need to be mocked.
However, there is an open item for Mockito here that discusses the issue.
While this doesn't answer your question, in general it tells you why you shouldn't need the feature at all or will allow you to join the conversation with your ideas.
Make sure to have mockito-inline dependency in your POM file
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>3.6.28</version>
<scope>test</scope>
</dependency>
In my case I had to test scenario where exception thrown static method encode() of URLEncoder Class, so for that
try (MockedStatic theMock = mockStatic(URLEncoder.class)) {
theMock.when(() -> URLEncoder.encode("Test/11", StandardCharsets.UTF_8.toString()))
.thenThrow(UnsupportedEncodingException.class);
when(restClient.retrieveByName("Test%2F11")).thenReturn(null);
Assertions.assertThrows(ResponseStatusException.class, ()->service.retrieveByName("Test/11"));
}
We can mock a static method by JMockit.
JMockit is used for mocking the external dependencies outside the test boundary, similar to Mockito and other such mocking libraries.
The most important feature of JMockit is that it lets us mock anything, even the things that are hard to mock with other libraries such as constructors, static and final methods. It even allows mocking the member fields and initialization blocks as well.
Follow the below steps to enable JMockit:
The JMockit artifact is located in the central Maven repository, add the JMockit dependency in pom.xml
<!-- https://mvnrepository.com/artifact/org.jmockit/jmockit -->
<dependency>
<groupId>org.jmockit</groupId>
<artifactId>jmockit</artifactId>
<version>1.49</version>
<scope>test</scope>
</dependency>
Mock the Class method in TestClass:
public class TestClass{
#Test
public void testMethod() {
new MockUp<ClassName>(){
#Mock
//mock the method here
};
}
}
Follow the tutorial to know more about how to use the JMockit.

Spring Boot - Rest Assured methods not found

I have a REST API built with Spring Boot.
I am attempting to use Rest-Assured test framework, however I can't seem to get it to work.
I am using the guide from Here
get("/lotto").then().assertThat().body("lotto.lottoId", equalTo(5));
And have added the dependencies to my maven project.
<dependency>
<groupId>com.jayway.restassured</groupId>
<artifactId>rest-assured</artifactId>
<version>2.9.0</version>
<scope>test</scope>
</dependency>
However, It doesn't seem to import the required classes and just prompts me to create a new "get()" method.
My Test class:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(Application.class)
#WebIntegrationTest
public class DemoControllerTest {
#Test
public void test() {
get("/lotto").then().assertThat().body("lotto.lottoId", equalTo(5));
}
}
What am I missing?
What am I missing?
A simple static import, that's missing! In order to resolve the get static method, just use the following static import:
import static com.jayway.restassured.RestAssured.get;
I had similar issue. What I did (using new version 3.0.2):
import io.restassured.RestAssured.*;
import io.restassured.matcher.RestAssuredMatchers.*;
import org.hamcrest.Matchers.*;
Instead of:
import static io.restassured.RestAssured.*;
import static io.restassured.matcher.RestAssuredMatchers.*;
import static org.hamcrest.Matchers.*;
So I had same issue couldnt find the methods ...

How to unset environment variable before testing in maven

I have some unit test which depends on environment variable.
I'd like to unset these environment variable before testing using maven.
Question: How can I achieve that?
Unfortunately, in this case you cannot use the environmentVariables option of the Maven Surefire Plugin, mainly because it would only work to add new environment variables but not override (or reset, which is actually equals to override to empty value) an existing variable.
Also note: an ant run wrapped in Maven and executed before the test would not work either.
The proposed solution is based on Java, especially on this approach proposed in another SO post. Although not advisable for application code, this hack may be acceptable for test code.
You could add the following class to your test code (under src/test/java):
package com.sample;
import java.lang.reflect.Field;
import java.util.Map;
public class EnvHack {
#SuppressWarnings("unchecked")
public static void resetEnvironmentVariable(String name, String value) throws Exception {
Class<?> processEnvironmentClass = Class.forName("java.lang.ProcessEnvironment");
Field theEnvironmentField = processEnvironmentClass.getDeclaredField("theEnvironment");
theEnvironmentField.setAccessible(true);
Map<String, String> env = (Map<String, String>) theEnvironmentField.get(null);
env.put(name, value);
Field theCaseInsensitiveEnvironmentField = processEnvironmentClass
.getDeclaredField("theCaseInsensitiveEnvironment");
theCaseInsensitiveEnvironmentField.setAccessible(true);
Map<String, String> cienv = (Map<String, String>) theCaseInsensitiveEnvironmentField.get(null);
cienv.put(name, value);
}
public static void main(String[] args) throws Exception {
resetEnvironmentVariable("test_prop", "test_value");
}
}
The class above is basically hacking Java API to change in memory the values of the environment variables. As such, they can be set to different values, reset and even unset actually (remove it from the map).
Since the class is now part of your test code, you have several options:
Use the class in the #Before methods (or #BeforeClass, with a certain difference) of a certain JUnit test case (before every JUnit method of the concerned class)
Use it within a JUnit test method (custom and narrowed usage)
Run its main method before any executed JUnit test (in a more global way) as explained below (and probably answering the question, even though other scenarios are also worth to mention, imho).
Let's have a look at each possible solution.
Use the class in the #Before methods of a certain JUnit test case
package com.sample;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class MainTest {
#Before
public void init() throws Exception {
EnvHack.resetEnvironmentVariable("test_prop", "test_value");
}
#Test
public void testEnvProperty() throws Exception {
String s = System.getenv("test_prop");
Assert.assertEquals(s, "test_value");
}
}
This solution can be used per test class and when a set of tests (methods) share the same requirements (suggestion: if they don't, it may be an hint, probably some method should be moved out).
Use it within a JUnit test method
package com.sample;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class MainTest {
#Before
public void init() throws Exception {
EnvHack.resetEnvironmentVariable("test_prop", "test_value");
}
#Test
public void testEnvProperty() throws Exception {
EnvHack.resetEnvironmentVariable("test_prop", "test_value2");
String s = System.getenv("test_prop");
Assert.assertEquals(s, "test_value2");
}
}
This solution gives the highest freedom: you can play with exactly the concerned variable exactly where required, although may suffer of code duplication it could also enforce tests isolation.
Run its main method before any executed JUnit test
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.4.0</version>
<executions>
<execution>
<phase>process-test-classes</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>com.sample.EnvHack</mainClass>
<classpathScope>test</classpathScope>
</configuration>
</execution>
</executions>
</plugin>
Note what we are doing in this case:
We are invoking the java goal of the Exec Maven Plugin to actually invoke the mainClass of our env hack class.
We are invoking it with classPathScope set to test in order to make it visible to the Enforcer Plugin
We are running it as part of the process-test-classes phase just to make sure it is executed before the test phase and hence before any test.
This solution centralizes the whole prepare environment procedure, once for all tests.
On the other side, you may also consider to use mocking in your tests. This is not a centralized option (unless you code it) but could give further hints and hence worth to mention. Here is a sample code resetting an environment variable via PowerMock
package com.sample;
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;
#RunWith(PowerMockRunner.class)
#PrepareForTest(System.class)
public class EnvTest {
#Before
public void init() {
PowerMockito.mockStatic(System.class);
Mockito.when(System.getenv("TEST_PROP")).thenReturn("TEST_VALUE");
}
#Test
public void test() {
String var = System.getenv("TEST_PROP");
Assert.assertEquals("TEST_VALUE", var);
}
}
This is similar to the first and second approach proposed above.
Note that to make it work you need to add the following dependencies to your POM:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-core</artifactId>
<version>1.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.5</version>
<scope>test</scope>
</dependency>
</dependencies>
General note: indeed having tests which are not fully isolated and depends on the existence (or the absence) of an environment variable is not advisable. You may run into maintenance issues or have more nasty troubleshooting, for colleagues or for your future yourself. So, if you really need it, better to properly document it.

Mock static Methods in ScalaTests or Spec2

I would like to use scala unit tests in my current maven/java project because of its BDD features. However, some of my model classes have static CRUDE methods such as Edit.save(). There is any technique where I can mock the implicit calls to inner static methods in Scala?
For regular Java tests this is easily done using PowerMock annotations. My question is how we can do the same using any scala test framework (ScalaTest, Specs2).
I learned that ScalaMock could help with that but the 'GeneratedMockFactory' trait is not present on their maven dependency (listed bellow). It seems to work only on SBT projects.
My scala test libraries:
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>2.10.0</version>
</dependency>
<dependency>
<groupId>org.scalatest</groupId>
<artifactId>scalatest_2.10</artifactId>
<version>2.0.M5b</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.scalamock</groupId>
<artifactId>scalamock-scalatest-support_2.10</artifactId>
<version>3.0.1</version>
</dependency>
This is how my scala tests looks like today:
package com.foo.edit
import org.scalatest._
import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
import com.foo.models.Edit
#RunWith(classOf[JUnitRunner]) // This line binds the test with maven-surefire runner
class EditSpec extends FlatSpec with ShouldMatchers {
"Edit" should "update name and website" in {
val document = new util.HashMap[String, String]() {
put("name", "A Name")
put("website", "www.domain.com")
}
// This is the test subject, it's a Java class
val edit = new Edit().modifiers(new util.HashMap[String, String]() {
put("name", "Another Name")
put("website", "http://site.org")
});
val result = edit.merge(document); // Here it calls Edit.save(document)
// Want to stub this.
assert(result.get("name") === "Another Name");
assert(result.get("website") === "http://site.org");
}
}

PowerMockito: got InvalidUseOfMatchersException when use matchers mocking static method

When I'm testing this static method
public class SomeClass {
public static long someMethod(Map map, String string, Long l, Log log) {
...
}
}
with
import org.apache.commons.logging.Log;
#RunWith(PowerMockRunner.class)
//#PrepareForTest(SomeClass.class)
public class Tests {
#Test
public void test() {
...
PowerMockito.mockStatic(SomeClass.class);
Mockito.when(SomeClass.someMethod(anyMap(), anyString(), anyLong(), isA(Log.class))).thenReturn(1L);
...
}
}
I got InvalidUseOfMatchersException. My questions are:
Why I got this exception when all the arguments are using matchers? How to solve it? I have debugged it, found the isA(Log.class) returns null.
When I add the #PrepareForTest annotation to the test class and run the test, the junit makes no response. Why?
EDIT
I tried not to use argument matchers, and got
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:
you stub either of: final/private/equals()/hashCode() methods.
Those methods cannot be stubbed/verified.
inside when() you don't call method on mock but on some other object.
at ...
So it seems due to the someMethod itself. There are synchronized block in the method. I'm wondering if Powermockito can mock that kind of method or not.
Try replacing the isA() to another any() call like this
Mockito.when(SomeClass.someMethod(anyMap(), anyString(), anyLong(), any(Log.class))).thenReturn(1L);
[EDIT]
Check your stacktrace when you get the exception. Are you seeing any NoClassDefFoundError reported? I noticed when I hadn't included the javassist.jar in my project I got a similar error to you.
I use PowerMockito and these are the jars I added to a brand new project to run the code that #Tom posted
powermock-mockito-1.4.10-full.jar
mockito-all-1.8.5.jar
javassist-3.15.0-GA.jar
junit-4.8.2.jar
common-logging-1.1.1.jar
Always a good idea to check that you're using compatible JAR versions, and also check if there are any other conflicting JARs in your projects classpath.
Better late than never, the line below:
Mockito.when(SomeClass.someMethod(anyMap(), anyString(), anyLong(),
isA(Log.class))).thenReturn(1L);
should be:
PowerMockito.when(SomeClass.someMethod(anyMap(), anyString(), anyLong(),
isA(Log.class))).thenReturn(1L);
So, instead of invoking Mockito.when, one should invoke PowerMockito.when
isA will always return null. This is by design, it is documented in the Javadoc for the isA() method. The reason for this is that null will always match the parameterized return type of class, which will always match the type of the argument in the stubbed method for which the isA() Matcher is used. The null value which is returned is not actually acted upon.
Seems to work fine for me. My complete test case:
import static org.mockito.Matchers.*;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.impl.SimpleLog;
import org.junit.Test;
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;
class SomeClass {
public static long someMethod(final Map map, final String string, final Long l, final Log log) {
return 2L;
}
}
#RunWith(PowerMockRunner.class)
#PrepareForTest(SomeClass.class)
public class InvalidUseOfMatchersTest {
#Test
public void test() {
// Mock the SomeClass' static methods, stub someMethod() to return 1
PowerMockito.mockStatic(SomeClass.class);
Mockito.when(SomeClass.someMethod(anyMap(), anyString(), anyLong(), isA(Log.class))).thenReturn(1L);
// null NOT is-a Log, uses default stubbing: returns 0
System.out.println(SomeClass.someMethod(null, null, 5L, null));
// SimpleLog passes is-a test, uses stubbed result: returns 1
System.out.println(SomeClass.someMethod(null, null, 7L, new SimpleLog("simplelog")));
}
}
Perhaps post a complete example to help diagnose what's going on?
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>${mockito.version}</version>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
<type>jar</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>${powermock.version}</version>
<type>jar</type>
<scope>provided</scope>
</dependency>
I hope your project is using maven. Try including these jars to the build.

Categories

Resources