1

I have some problems with unit testing the following method.

public List<GetSupplyChainResponse> getSupplyChains(){
    List<GetSupplyChainsResponse> response = new ArrayList<>();
    supplyChainRepository.findSupplyChainsWithCompound().forEach(result
            -> response.add(getGetSupplyChainSimpleResponse(result)));

    return response;
}

getGetSupplyChainSimpleResponse() is a private method of the same class as getSupplyChains()

Is there any possibility to define return values therefore or do you have any other ideas how I could test the method getSupplyChains()?

Honza Zidek
  • 9,204
  • 4
  • 72
  • 118
Lugo
  • 11
  • 1
  • You can use reflection. See this http://stackoverflow.com/questions/11282265/how-to-call-a-private-method-from-outside-a-java-class – Janos Binder Mar 03 '17 at 08:28
  • What are the problems that you are having? Are you getting any errors? Maybe you also want to show the `getGetSupplyChainSimpleResponse()` method so we can see what's going on in there. – px06 Mar 03 '17 at 09:13
  • You test the method `getSupplyChains()` exactly the same way you would test it if there wasn’t a lambda expression calling a `private` method. – Holger Mar 03 '17 at 18:50

2 Answers2

2

You might be overthinking this. The fact that the method that you want to test (getSupplyChains) uses a lambda that calls a private method is irrelevant: they are just implementation details.

What you unit test is the part of your class that you as a client interact with, i.e. its interface. You typically call a public method with some arguments (in this case there are none), you get some return value and that is what you verify in your unit test. If your public method makes use of some private method, it will be tested also.

The problem here is that the response that you get from getSupplyChains obviously depends on what supplyChainRepository.findSupplyChainsWithCompound() returns. What you do in this case is mock that dependency (supplyChainRepository) out: you create a mock instance of SupplyChainRepository, you tell it how to behave, and you pass it to this class, for example via the constructor.

You can either write the mock yourself, or you can rely on a mocking framework to do the heavy lifting like Mockito.

I definitely recommend against unit testing private methods (it leads to brittle tests), or increasing the visibility of those methods (a.k.a. sacrificing your design for the sake of testing).

Jan Van den bosch
  • 3,542
  • 3
  • 26
  • 38
-1

It is a commonly discussed problem, some prefer using reflection as Janos Binder recommended (How to call a private method from outside a java class), some can live with the fact the the visibility of the methods which we need to mock is increased for the sake of testability.

The problem is quite well discussed here: How do I test a class that has private methods, fields or inner classes?. You can see from the answers and wide discussions that the topic is quite complicated and developers split into factions and use different solutions.

I'd recommend you to remove the private access modifier and to make the method package private (use the default visibility). The common custom is to have the test classes in the same package (but not in the same folder!) as the tested classes.

This will allow you to:

  1. Test the getGetSupplyChainSimpleResponse() method itself, which you should do in any case somehow.

  2. Mock its behaviour for the purpose of testing getSupplyChains(). This you will achieve e.g. by using Mockito framework and its @Spy functionality.

For those who argue that this is "sacrificing your design for the sake of testing", I would answer that if you have private methods which need to be mocked and/or tested, then the design is not ideal anyway so changing the visibility to package private does not mean too big deterioration. The clear solution (from the object oriented design's point of view) is to delegate the behavior of such private methods to separate classes. However, sometimes in the real life it is just overkill.

Community
  • 1
  • 1
Honza Zidek
  • 9,204
  • 4
  • 72
  • 118