Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Test and mock an recursive method?

Tags:

java

mockito

I try to mock this method, but I can´t figure out how to structure this... I use Mockito and jUnit.

Is there some way to do this in a easy way?

Many thanks.


public Object getColumnValue(ReportRow row, String columnId, Vector errors) 
{

        if (HSBC_PAYREC.equals(columnId)) {
            String s = (String) getColumnValue(row, "Pay/Rcv", errors);
            if (s != null) {
                if (s.equals("Pay")) {
                    return "Receive";
                }
            }
            return "";
        }
}
like image 261
HenriqueAdriano Avatar asked Nov 02 '25 15:11

HenriqueAdriano


1 Answers

Because you're testing the system under test, it doesn't really ever make sense to mock the system under test. I think it's fair to say that this includes mocking recursive calls to the system under test. (Besides being philosophically very strange, you might have a very hard time ensuring that you're calling the unmocked version from the test while ensuring the unmocked version calls the mocked version when it tries to call itself.) Your best bet is to restructure your test so that the recursive behavior is part of your expected behavior.

That said, and though it does make the code harder to follow, you do have two alternatives. One involves a partial mock:

public Object getColumnValue(ReportRow row, String columnId, Vector errors) {
  if (HSBC_PAYREC.equals(columnId)) {
    String s = (String) getColumnValueRecursively(row, "Pay/Rcv", errors);
    if (s != null) {
      if (s.equals("Pay")) {
        return "Receive";
      }
      if (s.equals("Receive")) {
        return "Pay";
      }
      return "";
    }
  }
}

/** For testing. */
Object getColumnValueRecursively(ReportRow row, String columnId, Vector errors) {
  return getColumnValue(row, columnId, errors);
}

And in the test:

@Test public void yourTest() {
  SystemUnderTest yourSystemUnderTest = Mockito.spy(new SystemUnderTest());
  doReturn(yourMockedValue).when(yourSystemUnderTest).getColumnValueRecursively(
      /* your recursive base case */);
  // ...
}

Technically, I suppose you could do that without creating the indirect recursion, but then you're switching behavior based on parameters and that makes it harder to ensure your test is valid. Your other option is equally tricky:

SystemUnderTest recursiveInstance = this; // replace with a mock within test

public Object getColumnValue(ReportRow row, String columnId, Vector errors) {
  if (HSBC_PAYREC.equals(columnId)) {
    String s = (String) recursiveInstance.getColumnValue(row, "Pay/Rcv", errors);
    if (s != null) {
      if (s.equals("Pay")) {
        return "Receive";
      }
      if (s.equals("Receive")) {
        return "Pay";
      }
      return "";
    }
  }
}
like image 64
Jeff Bowman Avatar answered Nov 04 '25 08:11

Jeff Bowman



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!