I am getting an issue using powermockito (2.0.0-beta5) to verify a static method was called a certain number of times when I call a different (also static) method. The classes are prepared for test at the top of my test file The relevant code snippet is:
mockStatic(Tester.class);
when(Tester.staticMethod(anyString(), anyString())).thenAnswer(new FirstResponseWithText());
OtherClass.methodThatCallsTesterStaticMethod("", "", "", false, "");
verifyStatic(Tester.class, times(3));
Tester.sendFaqRequest(anyString(), anyString());
FirstResponseWithText is a class that extends Answer that controls the order of responses. I've used that elsewhere and it works fine.
I get the following error on the verifyStatic line:
org.mockito.exceptions.misusing.NotAMockException:
Argument passed to verify() is of type Class and is not a mock!
Make sure you place the parenthesis correctly!
See the examples of correct verifications:
verify(mock).someMethod();
verify(mock, times(10)).someMethod();
verify(mock, atLeastOnce()).someMethod();
What is the proper way to pass the class to verifyStatic? All the examples I can find online are for pre-2.x.x releases where verifyStatic did not take a class parameter.
I think the PowerMockito version is not the problem. I tested the following code with versions
1.7.3,
2.0.0-beta.5 (your version),
2.0.2.
Application classes:
package de.scrum_master.stackoverflow.q52952222;
public class Tester {
public static String sendFaqRequest(String one, String two) {
return "real response";
}
}
package de.scrum_master.stackoverflow.q52952222;
public class OtherClass {
public static void methodThatCallsTesterStaticMethod(String one, String two, String three, boolean four, String five) {
System.out.println(Tester.sendFaqRequest("A", "B"));
System.out.println(Tester.sendFaqRequest("C", "D"));
System.out.println(Tester.sendFaqRequest("E", "F"));
}
}
Test classes:
package de.scrum_master.stackoverflow.q52952222;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.util.Arrays;
public class FirstResponseWithText implements Answer {
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
String methodName = invocation.getMethod().getName();
return methodName + " called with arguments: " + Arrays.toString(args);
}
}
package de.scrum_master.stackoverflow.q52952222;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.times;
import static org.powermock.api.mockito.PowerMockito.*;
#RunWith(PowerMockRunner.class)
#PrepareForTest({ Tester.class })
public class MyTest {
#Test
public void myTest() {
// Tell PowerMockito that we want to mock static methods in this class
mockStatic(Tester.class);
// Stub return value for static method call
when(Tester.sendFaqRequest(anyString(), anyString())).thenAnswer(new FirstResponseWithText());
// Call method which calls our static method 3x
OtherClass.methodThatCallsTesterStaticMethod("", "", "", false, "");
// Verify that Tester.sendFaqRequest was called 3x
verifyStatic(Tester.class, times(3));
Tester.sendFaqRequest(anyString(), anyString());
}
}
So if this does not work for you and your Maven or Gradle dependencies are also okay, the difference is maybe in your FirstResponseWithText class. Maybe you want to show it so we can all see what kind of magic you do in there. As you can see from my sample code, I had to make several educated guesses because you only share a little snippet of test code and not MCVE like you should. This way I can only speculate and actually I do not like to because it might be a waste of time for both you and myself.
Related
I am writing a unit test for a class which extends from abstract class, one function of sub class will call super void function and function.
//two classes in different package
public abstract class AbstractBo {
private Map myMap = new HashMap();
protected void load(String jsonString) {
//convert the jsonString parameter to map, and set to myMap
}
public String getItem(String key) {
return myMap(Key);
}
}
public class SubBo extends AbstractBo {
public String submit(String initString) {
//init a map by super function
this.load(initString);
return this.getItem("myName");
}
}
I just want to test submit function, mock the load and getItem function. I am new to powermock.
As a rule of thumb, you should try to avoid Powermock as far as you can, since it's usually associated with bad code design.
You actually don't need to use it in this case, since Mockito covers this case with spies, which is itself a bad code smell as stated in the documentation.
My suggestion is to change the code using composition over inheritance.
Still, if you are stuck with the code as it is, just add Junit and Mockito to your dependencies and write a test like:
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
#RunWith(MockitoJUnitRunner.class)
public class SubBoTest {
#Test
public void shouldBehaveAsExpected() {
final String expected = "expected String";
final String testString = "test string";
final SubBo bo = spy(SubBo.class);
doNothing().when(bo).load(eq(testString));
when(bo.getItem("myName")).thenReturn(expected);
final String actual = bo.submit(testString);
assertEquals(expected, actual);
}
}
Again, please be reminded that this is not the proper way to go, and refactoring is advisable.
I am trying to test the method 'reallyCoolMethod' gets called with the correct parameters. The problem is stubbing the 'getSillyString' method is causing errors. When the method 'getSillyString' is private, the test will fail on the second doReturn:
doReturn("superSilly").when(spy, "getSillyString", 5, false);
however when the method is protected, the test will pass. The error is:
org.mockito.exceptions.misusing.UnfinishedStubbingException:
Unfinished stubbing detected here:
-> at org.powermock.api.mockito.internal.PowerMockitoCore.doAnswer(PowerMockitoCore.java:36)
E.g. thenReturn() may be missing.
Examples of correct stubbing:
when(mock.isOk()).thenReturn(true);
when(mock.isOk()).thenThrow(exception);
doThrow(exception).when(mock).someVoidMethod();
Hints:
1. missing thenReturn()
2. you are trying to stub a final method, you naughty developer!
Here is the class under test:
import java.util.Random;
#Singleton
public class FooBar {
#Inject
public FooBar(Foo foo, Bar bar) {
this.foo = foo;
this.bar = bar;
}
#POST
#Path("foos/")
public fooAction createFoo() {
word1 = getSillyString(4, true);
word2 = getSillyString(5, false);
int id = reallyCoolMethod(word1,word2);
}
private String getSillyString(int size, boolean allCaps){
}
}
Here is the test:
import static org.mockito.Mockito.verify;
import org.junit.Test;
import org.mockito.Mockito;
import static org.powermock.api.mockito.PowerMockito.doReturn;
import static org.powermock.api.mockito.PowerMockito.spy;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.modules.junit4.PowerMockRunner;
#RunWith(PowerMockRunner.class)
public class FooTest {
#Test
public void testCreateFoo() throws Exception {
Foo foo = Mockito.mock(Foo.class);
Bar bar = Mockito.mock(Bar.class);
FooBar resource = new FooBar(foo, bar);
FooBar spy = spy(resource);
doReturn("reallySilly").when(spy, "getSillyString", 4, true);
doReturn("superSilly").when(spy, "getSillyString", 5, false);
doReturn(1).when(spy).reallyCoolMethod(Mockito.anyString(),Mockito.anyString());
spy.createFoo();
verify(spy).reallyCoolMethod(Mockito.eq("reallySilly"),Mockito.Eq(superSilly));
}
}
The real answer here: you created hard-to-test code by putting an essential element of that class into a private method.
In other words: if that thing is so essential to your production code, then the better answer is to put the underlying functionality in its own class. So, you create some interface:
public interface SillyService {
public String getSillyString();
}
And then you use dependency injection to provide "some kind" of implementation to the class that needs this service.
The desire to mock a private method always is a consequence of a bad design decision. Thus the answer does not lie within Mockito or PowerMock, but by stepping back and improving that design.
For starters, one can watch these videos to learn how to write testable code instead.
And beyond that: please note that PowerMock impacts the things you can do with Mockito - as PowerMock comes with pretty much backlevel versions of Mockito. My personal recommendation: get rid of your need for PowerMock; and instead just use latest/greatest versions of Mockito.
Trying to stub a class with 2 possible invocation/return paths using custom Matcher ... ran into an interest problem.
Here is a test I wrote to illustrate ...
This might be difficult to implement, but I would have expected the 1st ArgumentMatcher is not invoked when stubbing the second when(...).thenReturn(...)
But running the code below prints foobar on stdout. Is there anything we can do to prevent this behavior? Or am I using the wrong pattern by trying to stub a single mock with multiple custom ArgumentMatcher
FYI - powermock is on my classpath for other tests (not sure if that matters but I do see it in the stack trace)
import org.junit.Test;
import org.mockito.ArgumentMatcher;
import java.io.File;
import java.io.FilenameFilter;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class MyTest {
#Test
public void name() throws Exception {
File file = mock(File.class);
when(file.list(argThat(new ArgumentMatcher<FilenameFilter>() {
#Override
public boolean matches(Object argument) {
System.out.println("foobar");
return 1 + 1 >2;
}
}))).thenReturn(null);
// at this point, mockito will attempt to run the previous matcher, treating this stub code as invocation ... and printing out 'foobar'
when(file.list(argThat(new ArgumentMatcher<FilenameFilter>() {
#Override
public boolean matches(Object argument) {
System.out.println("barbar");
return true;
}
}))).thenReturn(null);
}
}
EDIT added comments to help illustrate
If you use the doReturn() syntax, then the method is not called.
doReturn(null).when(file).list(argThat(new ArgumentMatcher<FilenameFilter>() {
#Override
public boolean matches(Object argument) {
System.out.println("barbar");
return true;
}
}));
See this answer for more details. Also, the docs explain this use-case (emphasis mine):
You can use doReturn(), [...] in place of the corresponding call with when(), for any method. It is necessary when you:
stub void methods
stub methods on spy objects (see below)
stub the same method more than once, to change the behaviour of a mock in the middle of a test.
I have:
package com.darlik.test;
import org.junit.Assert;
public class Test {
public static void main(String[] args) {
assertTrue(1, 2);
}
}
package with org.junit is set and working but in line with assertTrue i have error:
The method assertTrue(int, int) is undefined for the type Test
Why? I use Eclipse.
assertTrue is based on a single boolean condition. For example
assertTrue(1 == 2);
You need to import the statement statically to use
import static org.junit.Assert.assertTrue;
Typically, however assertEquals is used when comparing 2 parameters, e.g.
public class MyTest {
#Test
public void testAssert() throws Exception {
assertEquals(1, 2);
}
}
You have to specify the class that defines that method:
Assert.assertTrue(condition);
Furthermore you're calling the method with 2 parameters which makes no sense. assertTrue expects a single boolean expression.
Although you can also do this by using a static import:
import static org.junit.Assert.*;
which will allow you to call it as assertTrue(condition); instead.
From the doc : assertTrue(boolean) or assertTrue(String, boolean) if you want to add a message.
AssertTrue assert that a condition is true, you still have to code such condition for it to be evaluated at runtime.
Better try assertThat with matchers. Check this blog about it https://objectpartners.com/2013/09/18/the-benefits-of-using-assertthat-over-other-assert-methods-in-unit-tests/
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
....
#Test
public void testNum() {
assertThat(repo.getNum(), is(equalTo(111)));
}
Delete the module-info.java file if there is any in the project. It's not necessary and is only used if you're using Java's built-in module system.
This is the test:
import static junit.framework.Assert.assertTrue;
import static org.powermock.api.mockito.PowerMockito.mock;
import static org.powermock.api.mockito.PowerMockito.whenNew;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
#RunWith(PowerMockRunner.class)
#PrepareForTest( {ClassUnderTesting.class} )
public class ClassUnderTestingTest {
#Test
public void shouldInitializeMocks() throws Exception {
CollaboratorToBeMocked mockedCollaborator = mock(CollaboratorToBeMocked.class);
suppress(constructor(CollaboratorToBeMocked.class, InjectedIntoCollaborator.class));
whenNew(CollaboratorToBeMocked.class)
.withArguments(InjectedAsTypeIntoCollaborator.class)
.thenReturn(mockedCollaborator);
new ClassUnderTesting().methodUnderTesting();
assertTrue(true);
}
}
These are the classes :
public class ClassUnderTesting {
public void methodUnderTesting() {
new CollaboratorToBeMocked(InjectedAsTypeIntoCollaborator.class);
}
}
public class CollaboratorToBeMocked {
public CollaboratorToBeMocked(Class<InjectedAsTypeIntoCollaborator> clazz) {
}
public CollaboratorToBeMocked(InjectedIntoCollaborator someCollaborator) {
}
public CollaboratorToBeMocked() {
}
}
public class InjectedAsTypeIntoCollaborator {
}
public class InjectedIntoCollaborator {
}
This is the error :
org.powermock.reflect.exceptions.TooManyConstructorsFoundException: Several matching constructors found, please specify the argument parameter types so that PowerMock can determine which method you're refering to.
Matching constructors in class CollaboratorToBeMocked were:
CollaboratorToBeMocked( InjectedIntoCollaborator.class )
CollaboratorToBeMocked( java.lang.Class.class )
Here comes the question: how can I make PowerMock figure out what constructor to look for?
The problematic line is the suppress. That is where the error comes from.
Perhaps it is too late for your question. I met it today and found the solution at the following url. Basically, you need to specify your argument type like.
whenNew(MimeMessage.class).**withParameterTypes(MyParameterType.class)**.withArguments(isA(MyParameter.class)).thenReturn(mimeMessageMock);
http://groups.google.com/group/powermock/msg/347f6ef1fb34d946?pli=1
Hope it can help you. :)
I didn't know of PowerMock until you wrote your question, but did some reading and found this in their documentation. Still I am not really sure if that helps you:
If the super class have several
constructors it's possible to tell
PowerMock to only suppress a specific
one. Let's say you have a class called
ClassWithSeveralConstructors that has
one constructor that takes a String
and another constructor that takes an
int as an argument and you only want
to suppress the String constructor.
You can do this using the
suppress(constructor(ClassWithSeveralConstructors.class, String.class));
method.
found at http://code.google.com/p/powermock/wiki/SuppressUnwantedBehavior
Isn't it the thing you wanted?
EDIT: Now I see, you've already tried suppressing. But are you sure you got the suppress call right? Isn't the first argument of constructor() supposed to be the class you would like to surpress the constructor in?
If using PowerMock for EasyMock you can do PowerMock.expectNew(CollaboratorToBeMocked.class, new Class[]{InjectedIntoCollaborator.class}, ...) where the Class[] is the parameter types of the constructor you're expecting to be called. This will resolve the ambiguity between the constructors.