0

Class that i want to mock:

TestClass.java

public class testClass(){

    public String getDescription(String input){
        String value = this.getDetails(input); // i am not going to change this line, hence want to mock this.
       //below this i have some complexity logic, which i would like to fix cyclomatic complexity issue            
    }

    private String getDetails(String input){
        return "More details for the "+input;
    }
}

My questions is how do i mock "this.getDetails(input)" to return some string for testing purpose?

Juan Cruz Soler
  • 8,172
  • 5
  • 41
  • 44
Barani r
  • 2,119
  • 1
  • 25
  • 24
  • 4
    You don't! Don't mock out parts of the class under test, otherwise you tie your tests very tightly to the current implementation rather than the desired behaviour, making it harder to refactor later. – jonrsharpe Oct 31 '17 at 15:47
  • Yeah, i got it. thanks for the quick response. I have edited my question. What i am trying to do is, just mock that line alone, so that i can fix that method getDescription(). – Barani r Oct 31 '17 at 15:55
  • And I'm saying *you shouldn't do that*. You don't want to have to rewrite the tests just because you decide later to inline `String value = "More details for the " + input;`, for example. Write a test for the whole behaviour you expect from `getDescription` for a specific input; *that's* the public interface, *that's* the unit you're testing. – jonrsharpe Oct 31 '17 at 15:56
  • let me put it other way, if i want to do it, how to do it? :) i know, i should not do it, just curious to know. – Barani r Oct 31 '17 at 15:58
  • 1
    Possible duplicate of [Testing Private method using mockito](https://stackoverflow.com/questions/8799439/testing-private-method-using-mockito) – jonrsharpe Oct 31 '17 at 16:00
  • If you're so curious, I'd recommend *looking it up*. – jonrsharpe Oct 31 '17 at 16:01

2 Answers2

1

If you've got a class that is big and complex enough that you need to mock a small piece of it, take that as a hint that you're violating the Single Responsibility Principle and properly split up the classes. If you use dependency injection, you can then supply whatever implementation you'd like.

public class TestClass {
    /**
      * Computes a detail string based on an input. Supply this in the constructor
      * for full DI, relax visibility, or add a setter.
      */
    private final Function<String, String> detailFunction;

    public String getDescription(String input){
        String value = detailFunction.apply(input);
        // ...
    }
}

As a lightweight alternative, you can test an override or spy of your actual class.

@Test public void testTestClassWithOverride() {
  TestClass instanceUnderTest = new TestClass() {
    @Override public String getDescription(String input) {
      return "Predictable value";
    }
  };
  // test your instanceUnderTest here
}

@Test public void testTestClassWithSpy() {
  TestClass spyUnderTest = Mockito.spy(new TestClass());
  doReturn("Predictable value").when(spyUnderTest).getDescription(anyString());
  // test your spyUnderTest here
}

Bear in mind that, though this is an option for you, it shouldn't be your first option: Rather than testing your actual class, you're testing a one-off variant of it, and you've made it so other consumers can subclass your TestClass as well. If possible, write the flexibility you need into the class itself and treat your test as a consumer that plays by the same rules.

Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
1

First of all, it is a bad practice to make a so-called "partials mocks". This illustrates that your code doesn't follow single responsibility principle that leads to your code being not (or hardly) testable.

I would suggest you to extract getDescription method from your class and use it indirectly via dependency inversion or more concrete - dependency injection (for instance by employing Spring Framework):

public class TestClass() {

    private DetailsServiceProvider detailsServiceProvider;

    public TestClass(DetailsServiceProvider detailsServiceProvider) {
        this.detailsServiceProvider = detailsServiceProvider;
    }

    public String getDescription(String input) {
        String value = detailsServiceProvider.getDetails(input); // i am not going to change this line, hence want to mock this.
       //below this i have some complexity logic, which i would like to fix cyclomatic complexity issue            
    }
}

public interface DetailsServiceProvider {
    String getDetails(String input);
}

public class DetailsServiceProviderImpl implements DetailsServiceProvider{

    @Override
    public String getDetails(String input) {
        return "More details for the "+input;
    }
}

Then in your test, you could simply:

@Test 
public void test() {
  DetailsServiceProvider mockedProvider = Mockito.mock(DetailsServiceProvider.class);

  //TODO: add scenarios for the mocked object

  TestClass target = new TestClass(mockedProvider);

  String description = target.getDescription();

  //TODO: add assertions
}

If you do not want to struggle with the preferred approach you could use @Spy in Mockito. This will create exactly what you want - a partial mock for your object where part of the methods will be real and another part - mocks:

@Test 
public void test() {

  TestClass partialMockedObject = Mockito.spy(new TestClass());

  Mockito.doReturn("test details").when(partialMockedObject).getDetails();

  String description = partialMockedObject.getDescription();

  //TODO: add assertions

}

Again, this method is not desired but can be used if no other options are given. Note that this requires getDetails() to be visible in tests, meaning that the private modifier won't work here.

Danylo Zatorsky
  • 5,856
  • 2
  • 25
  • 49