简体   繁体   中英

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();
  }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM