JMockit: How to override already mocked method with a new mock? - java

I think I might have found a bug in JMockit, but I would like some to confirm whether it's a bug or there's something I'm missing.
I have the following (very simple) class:
public class Dummy {
public void foo() {System.out.println("O");}
}
Now I have the following tests, where in each of them I try to mock the method 'foo' more than once (each test does it a little differently):
Test #1
#Test
public void test1() {
new MockUp<Dummy>() {
#Mock
public void foo(Invocation inv) {
System.out.println("A");
inv.proceed();
}
}
new MockUp<Dummy>() {
#Mock
public void foo(Invocation inv) {
System.out.println("B");
inv.proceed();
}
}
new Dummy().foo();
}
Test #2
#Test
public void test2() {
mock("A");
mock("B");
new Dummy().foo();
}
private void mock(final String s) {
new MockUp<Dummy>() {
#Mock
public void foo(Invocation inv) {
System.out.println(s);
inv.proceed();
}
}
}
The only difference between the tests is the extraction of the mock code to a different method. But the results are not the same...
Test #1 output:
B
A
B
O
This is odd, because I wouldn't expect A to appear at all. But anyway, here's test #2 output:
B
A
A
A
...ad infinitum
Test #2 will fail with a StackOverflowError.
Is this a bug or am I missing something?
Update (with the solution)
As #Rogério mentioned, this behavior is not acceptable.
Then how can the mock be overridden? like this:
private MockUp<Dummy> mock;
#Test
public void test3() {
mockCorrectly("A");
mockCorrectly("B");
new Dummy().foo();
}
private void mockCorrectly(final String s) {
if (mock != null) {
mock.tearDown();
}
mock = new MockUp<Dummy> {
#Mock
public void foo(Invocation inv) {
System.out.println(s);
inv.proceed();
}
}
}
And for the output:
B
O
Great :)

It's not clear what exactly happens here; apparently, at runtime some "chained mocking" is occurring.
The real problem is that both tests are doing something invalid with the MockUp API: they are mocking the same method in the same class twice in the same test. It is ok to have two different mock-ups for the same class in the same test, as long as they mock different methods/constructors.
The resulting behavior is undefined, as JMockit does not support multiple simultaneous mockings of the same method.

Related

Error when using the same string in different tests

I have the following scenario:
I perform several tests (#Test) and tests in Cucumber, in Selenium Webdriver, Java.
The tests are going well. However, I want to leave a string stored in one #Test (public void) in another #Test (public void). I cannot.
Could anyone help?
First test:
#Test
public void testDocuments() {
OneClass oneClass = new OneClass();
oneClass.one();
oneClass.two();
}
Second test:
#Test
public void testDocuments() {
OneClass oneClass = new OneClass();
oneClass.one();
oneClass.two();
}
Method one
public String one() {
if (this.cnpj == null) {
this.cnpj = add.cnpj(false);
} else {
}
return this.cnpj;
}
Both tests I want you to use the same generated string !!!!
I look forward and thanks in advance!
I'm not sure what your method one() does, but assuming you want to use the same value for two different tests, why not just do this:
OneClass oneClass = new OneClass();
String yourGeneratedString = oneClass.one();
// First test
#Test
public void testDocuments() {
yourFunction(yourGeneratedString);
}
// Second test
#Test
public void testDocuments2() {
yourOtherFunction(yourGeneratedString);
}
If I understand correctly, you need this.cnpj value to be available within the second test?
Each time you do new OneClass() , it creates a new instance of it.
So you can do one of the following:
Use singleton instance of OneClass
Make cnpj a static field within OneClass
If I understand it right, you want to share data from one test to second one. If you user testNG then you can do it this way.
import org.testng.ITestContext;
import org.testng.annotations.Test;
public class MyTest {
#Test
public void testOne(ITestContext context){
context.setAttribute("myKey", "myValue");
}
#Test
public void testTwo(ITestContext context){
String valueFromTestOne = (String) context.getAttribute("myKey");
System.out.println("My key = " + valueFromTestOne);
}
}

How to undo/reset PowerMockito.mockStatic?

Assuming I have the following classes
public class StaticClass {
public static void staticMethod() throws SomeException {
System.out.println("staticMethod");
}
private StaticClass() {
}
}
and
public class SomeClass {
public void someMethod() {
try {
StaticClass.staticMethod();
}catch(SomeException ex) {
System.out.println("SomeException occurred");
return;
}
System.out.println("SomeException didn't occur");
}
}
which I'm testing with
#RunWith(PowerMockRunner.class)
#PrepareForTest(StaticClass.class)
public class SomeClassTest {
#Test
public void testStaticMethod() throws Exception {
mockStatic(StaticClass.class);
doThrow(new SomeException("unimportant message")).when(StaticClass.class,
"staticMethod");
//test something where exception is needed
SomeClass instance = new SomeClass();
try {
instance.someMethod();
fail("IllegalStateException expected");
}catch(IllegalStateException expected) {
}
//now test something where exception isn't needed
instance.someMethod();
}
}
How can I undo the static mocking/the configuration to throw SomeException so that I can test the code after the try-catch block in the second instance.someMethod()?
PowerMock: How to unmock a method? doesn't apply because there's no mock reference to pass to Mockito.reset and passing StaticClass causes java.lang.ClassCastException: java.lang.Class cannot be cast to org.mockito.internal.creation.bytebuddy.MockAccess.
SomeException simply extends Exception.
A SSCCE is provided at https://gitlab.com/krichter/powermock-undo-statik-mocking.
I'm using PowerMock 1.7.3.
My opinion, but in general a unit test should exercise a single code path. (I think of this as applying single responsibility to a test method.)
Bu my suggestion about splitting the tests does solve the problem. I don't know the details, but #PrepareForTest provides a fresh StaticClass for each test.
These separate tests work:
#Test
public void testStaticMethodWhenSomethingUnexpectedHappens() throws Exception {
mockStatic(StaticClass.class);
// changed exception type
doThrow(new IllegalStateException("unimportant message")).when(StaticClass.class, "staticMethod");
SomeClass instance = new SomeClass();
try {
instance.someMethod();
fail("IllegalStateException expected");
} catch (IllegalStateException expected) {
}
// added verification
verifyStaticMethodWasInvokedOneTime();
}
#Test
public void testStaticMethodHappyPath() throws Exception {
mockStatic(StaticClass.class);
doNothing().when(StaticClass.class, "staticMethod");
SomeClass instance = new SomeClass();
instance.someMethod();
// added verification
verifyStaticMethodWasInvokedOneTime();
}
private void verifyStaticMethodWasInvokedOneTime() throws SomeException {
verifyStatic(StaticClass.class);
StaticClass.staticMethod();
}
For anyone wondering how to reset PowerMocks e.x. for those pesky private static final loggers...
There was an issue (see Karl's comment in accepted solution) addressing it and the solution was to use #PrepareForTest in the method level.
so in your test method you need the annotation
/*
* Test for MyClass.java which uses a private static final logger
*/
public class MyStaticMockTest {
static final Logger logger = PowerMockito.mock(Logger.class);
#BeforeClass
public static void setup() {
PowerMockito.mockStatic(LoggerFactory.class);
PowerMockito.when(LoggerFactory.getLogger(MyClass.class))
.thenReturn(MyStaticMockTest.logger);
}
#Test
#PrepareForTest({LoggerFactory.class, Logger.class})
public void testit() {
MyClass mc = new MyClass();
mc.methodWithSomeLogging();
Mockito.verify(MyStaticMockTest.logger).info("some message");
}
#Test
#PrepareForTest({LoggerFactory.class, Logger.class})
public void testit() {
MyClass mc = new MyClass();
mc.anotherMethodWithSomeLoggingButUsingSameMessage();
//Method will pass and not complain about info being called 2x
Mockito.verify(MyStaticMockTest.logger, Mockito.times(1)).info("some message");
}
}
if you want every single method reset, just put the #PrepareForTest decorator on the class instead of the method

Mocking chained methods calls using PowerMock

I have a class which I would like to test with a public static method that contains some chained method calls. Assuming that an exception occurs during the chained method calls, how do I handle this effectively and make it return some specific value?
Following is the code sample of the test class.
#RunWith(PowerMockRunner.class)
#PrepareForTest({CodeWithPrivateMethod.class,CodeWithAnotherPrivateMethod.class,CodeWithYetAnotherPrivateMethod.class})
public class CodeWithPrivateMethodTest {
#Test
public void when_gambling_is_true_then_always_explode() throws Exception {
CodeWithYetAnotherPrivateMethod codeWithYetAnotherPrivateMethod = PowerMockito.spy(new CodeWithYetAnotherPrivateMethod());
PowerMockito.whenNew(CodeWithYetAnotherPrivateMethod.class).withAnyArguments().thenReturn(codeWithYetAnotherPrivateMethod);
CodeWithAnotherPrivateMethod codeWithAnotherPrivateMethod = PowerMockito.spy(new CodeWithAnotherPrivateMethod());
PowerMockito.whenNew(CodeWithAnotherPrivateMethod.class).withAnyArguments().thenReturn(codeWithAnotherPrivateMethod);
PowerMockito.doReturn(true).when(codeWithYetAnotherPrivateMethod, "getGambling");
//PowerMockito.doReturn(codeWithYetAnotherPrivateMethod).when(codeWithAnotherPrivateMethod, "getGambleValue");
PowerMockito.spy(CodeWithPrivateMethod.class);
CodeWithPrivateMethod.startGamble();
}
}
Following is the code sample for the class under test
public class CodeWithPrivateMethod {
public static void startGamble() {
Boolean gamble = CodeWithAnotherPrivateMethod.getGambleValue()
.getGambling();
if (gamble) {
System.out.println("kaboom");
}else{
System.out.println("boom boom");
}
}
}
Following is the code sample for the class that gets called from the class under test
public class CodeWithAnotherPrivateMethod {
static CodeWithYetAnotherPrivateMethod codeWithYetAnotherPrivateMethod = new CodeWithYetAnotherPrivateMethod();
public static CodeWithYetAnotherPrivateMethod getGambleValue() {
return codeWithYetAnotherPrivateMethod; //works fine
return null; // fails
}
}
Following is the code sample for the other class that gets called from the class under test
public class CodeWithYetAnotherPrivateMethod {
public Boolean getGambling() {
return false;
}
}
So Assuming I return a null value from getGambleValue() method of CodeWithAnotherPrivateMethod class, how do I handle this null value effectively in my testclass?
This is how to specify expected exceptions using Mockito:
#Test(expected = NullPointerException.class)
public void when_gambling_is_true_then_always_explode() throws Exception {
...
Before I found out about this I would do:
#Test
public void when_gambling_is_true_then_always_explode() throws Exception {
// setup omitted
try {
CodeWithPrivateMethod.startGamble();
}
catch(NullPointerException e) {
// expected
return;
}
fail("Expected NullPointerException");
}
EDIT: Testing multiple classes that call each other statically like this is a severe code smell. Unit tests should test a single class and inline static calls should be limited to utility classes.
Another comment: your example class names are very confusing. Next time please stick with Foo, Bar, Baz or Appple, Pear, Banana.
If you are not getting an NPE then I expect your mocking/spying is interfering. If you call the code under test without mocking/spying the call chain would be:
CodeWithPrivateMethod.startGamble();
->
CodeWithYetAnotherPrivateMethod value = CodeWithAnotherPrivateMethod.getGambleValue();
->
return null;
<-
value.getGambling();
<- throws NullPointerException
What exactly are you trying to find out or achieve?
EDIT: Here's how it should work with PowerMock
#RunWith(PowerMockRunner.class)
#PrepareForTest(CodeWithAnotherPrivateMethod.class)
public class CodeWithPrivateMethodTest {
#Mock
private CodeWithYetAnotherPrivateMethod yetAnotherInstance;
#Test
public final void testStartGamble() {
// SETUP
mockStatic(CodeWithAnotherPrivateMethod.class);
expect(CodeWithAnotherPrivateMethod.getGambleValue())
.andReturn(yetAnotherInstance);
Boolean gamblingValue = true;
expect(yetAnotherInstance.getGambling()).andReturn(gamblingValue);
replayAll();
// CALL
CodeWithPrivateMethod.startGamble();
// VERIFY
verifyAll();
}

Mock a single static method using PowerMock and TestNG

class StaticClass {
public static String a(){ return "a"; }
public static String ab(){ return a()+"b"; }
}
I want to mock StaticClass::a so that it returns "x" and the call to StaticClass.ab() results in "xb"...
I find it very hard in PowerMock and TestNG...
the exact code I am testing righ now:
class StaticClass {
public static String A() {
System.out.println("Called A");
throw new IllegalStateException("SHOULD BE MOCKED AWAY!");
}
public static String B() {
System.out.println("Called B");
return A() + "B";
}
}
#PrepareForTest({StaticClass.class})
public class StaticClassTest extends PowerMockTestCase {
#Test
public void testAB() throws Exception {
PowerMockito.spy(StaticClass.class);
BDDMockito.given(StaticClass.A()).willReturn("A");
assertEquals("AB", StaticClass.B()); // IllegalStateEx is still thrown :-/
}
}
I have Maven dependencies on:
<artifactId>powermock-module-testng</artifactId>
and
<artifactId>powermock-api-mockito</artifactId>
Why not try something like :
PowerMockito.mockStatic(StaticClass.class);
Mockito.when(StaticClass.a()).thenReturn("x");
Mockito.when(StaticClass.ab()).thenCallRealMethod();
I think this can be accomplished with a Partial Mock.
PowerMock.mockStaticPartial(Mocked.class, "methodToBeMocked");
This might be of help: http://avricot.com/blog/index.php?post/2011/01/25/powermock-%3A-mocking-a-private-static-method-on-a-class

Mockito & Junit null pointer exception: name must not be null

I have been trying to run the following test using mockito and junit and I keep on getting "java.lang.NullPointerException: name must not be null"
Can anyone tell me why this is happening?
On debugging, I found out that this exception is thrown when the test executes the following statement in isStopValid(String) method:
FacilityValidationUtil facUtil = new FacilityValidationUtil();
#RunWith(MockitoJUnitRunner.class)
public class MyFormTest{
#InjectMocks MyForm form = new MyForm();
#Mock FacilityValidationUtil facUtil;
#Test
public void testIsStopValid() throws FinderException{
when(facUtil.isFacilityValid("")).thenReturn(false);
form.setOrigin("");
assertEquals(false, form.isStopValid(form.getOrigin()));
}
}
Class with method to be tested:
public class MyForm{
FacilityValidationUtil facUtil = new FacilityValidationUtil();
public boolean isStopValid(String stop){
try {
return facUtil.isFacilityValid(stop);
} catch (FinderException e) {
log.error("Error finding the stop. "+e.getCause());
return false;
}
}
}
public class FacilityValidationUtil{
private FacilityDAO facilityDao = new HibernateFacilityDAO();
public boolean isFacilityValid(String facility) throws FinderException{
boolean test;
FacilityImpl facilityImpl = facilityDao.findFacilityByNassCode(facility);
test = (facilityImpl==null)?false : true;
return test;
}
}
public class HibernateFacilityDAO extends HibernateAbstractDeltaDAO implements FacilityDAO {
public HibernateFacilityDAO() {
super(false);
}
}
Short Answer: You are trying to mock a variable (facUtil) that is local to your isStopValid method, so the mock version of this object in your test is never going to be called because you are 'newing it up" each time.
Long Answer: It looks like you are trying to mock the call to your FacilityValidationUtil class, and if this is the case, then you need to either make the class a field so that Mockito can inject the object by reflection (if this object is thread safe, which it looks like it is) or explore a mocking framework like PowerMockito that will allow you to mock a constructor (google for PowerMockito when new).
PowerMockito.whenNew(FacilityValidationUtil.class).withNoArguments().thenReturn(facUtil);
Mockito doesn't support any mocking of constructor args by default.
EDIT
If you are still having trouble, then I would suggest starting with a smaller example. I've put together one for you that works and uses the code you are trying to test (It's using inner classes though, which Mockito has some quirky rules about, but I'm just doing it to compress the example).
#RunWith(MockitoJUnitRunner.class)
public class MyFormTest {
#InjectMocks
private MyForm form = new MyForm();
#Mock
private FacilityValidationUtil facUtil;
#Test
public void testIsStopValid_false() {
when(facUtil.isFacilityValid("")).thenReturn(false);
assertEquals(false, form.isStopValid(""));
}
#Test
public void testIsStopValid_true() {
when(facUtil.isFacilityValid("")).thenReturn(true);
assertEquals(true, form.isStopValid(""));
}
public class MyForm {
private FacilityValidationUtil facUtil = new FacilityValidationUtil();
public boolean isStopValid(String stop) {
try {
return facUtil.isFacilityValid(stop);
} catch (FinderException e) {
return false;
}
}
}
public class FacilityValidationUtil {
public boolean isFacilityValid(String facility) throws FinderException {
throw new RuntimeException(facility);
}
}
public class FinderException extends RuntimeException {
public FinderException(String message) {
super(message);
}
}
}
What's really important is that your mock is not getting injected correctly. Until you get that resolved, you are going to keep getting the same error. Set a break point in your MyForm at the point you call facUtil.isFaciltyValid and look at the object. It should be a mockito object, not your class.
Good luck.

Categories