0

I'm testing a "save in DB" public method that also sends some of that saved data modified to a service. The test assures that it is saving what it receives but is sending the modified data different. The problem comes when an internal private method calls a repository that is set as autowired its response is nullPointer as in the reflection that autowired repository is not initialized.

So first to assert what the private method answers I made a reflection on the private method.I mocked the inside autowired service with a controlled response but found no result on how set that mocked service answer on my private reflection.

I'm still new with testing so i might have make some mistakes explaining would do my best to correct it and expand on the topic further down.


This is for a public method "X" that saves something in database(Returns a code for the thing saved) then does some other stuff and then goes to a private method "Y" to verify some data and inside that private method goes to other private method "Z" ,to create some new data retriving data from another service "B" and from what it got before, to send that to an outside service "A". All of this is done in one same class implementation "classImpl". The private method "Z" recives the same as the "X" method and it generates and returns the data that is going to be send in "A" service using what it got from "B".


@Test
@Rollback(false)
@Transactional(readOnly = false)
TEST{

testData testXData= new testData();
testXData.set()...
testXData.set()...
.......Generate data to be saved.

mockResponse controlledOutput = mock(mockResponse.class);

deliverInMockResponse deliverInMockResponse = new deliverInMockResponse();
deliverInMockResponse.set()...
deliverInMockResponse.set()...
.......Generate data to be delivered in the mockResponse.

when(controlledOutput.findByAnd(otherSomething)).thenReturn(deliverInMockResponse);

assertNotNull(testSaveMethodX(testXData));
String Something = "evalutaThis";
assertEquals(testXData.getSomething(),Something);

try{
      classImpl publicImpl = new classImpl();
      Method method =  classImpl.class.getDeclaredMethod("Z", testData.class);
      method.setAccessible(true);
      methodZResponse Zoutput = (methodZResponse) method.invoke(publicImpl, testXData);
      assertNotEquals(Zoutput.getSomething(),Something);
    }
    catch (Exception e){
      assertNotNull(null);
    }

}
  • 1
    Wow sounds like `X` does way too much. You should be able to test your code by mocking dependencies and calling public methods. Mocking private methods should almost never need to be done as it is an internal implementation detail of your code. This is indicative of a larger architectural problem, and you need to fix that first instead of trying to hack around it by mocking out private implementations. This should only ever be a last resort for things like third party code which you cannot control... but even then you can write adapters to enable mocking of these contracts. – flakes Jun 03 '19 at 21:41
  • @flakes Its mostly validations and other cases that apply to the data that i want to save in DB. The thing is that the data that is going to be send to the service is generated only in the inside private method. I don't know what an adapters is if you could give some references to read about it i would really appreciate it. – kalolomagno Jun 03 '19 at 21:53
  • @kalolomagno - an adapters is a design pattern - https://refactoring.guru/design-patterns/adapter – Pete Jun 04 '19 at 08:47

2 Answers2

0

As for private method mocking or testing, I would rather suggest you try PowerMock instead of write reflection by yourself. here is a good guide. Also Mock private method using PowerMockito

Qingfei Yuan
  • 1,196
  • 1
  • 8
  • 12
0

If you're running the made-by-yourself reflection code inside the test, then probably the tested code can be done better.

Not all the code is automatically unit-testable. In order to be able to test the code, you should write it in a certain way.

You've shown the test in a code snippet in a question, but the code that you test is also very important here. From the first paragraph of the question I assume you have something like this:

class MyDao {
   @Autowired
   private SomeService service;

   public void saveInDb() {
     // do something with db
     service.sendData();
   }
}

Since Autowiring doesn't happen during the test (because in a unit test you don't have Spring to make its magic), you have a hard time testing saveInDb method

However, this code is problematic and can be refactored for better unit testing:

So here is the tip: do not use Autowiring on fields, instead rewrite the code with Constructor Injection:

class MyDao {
    private SomeService service;
    @Autowired // you can even omit this annotation in recent versions of spring
               // spring will understand what should be injected if there is only one 
               // constructor in the class
    public MyDao(SomeService service) {
        this.service = service;
    }
} 

If you feel like this is a lot of code, you can even use Lombok project to generate a constructor for you, but it's beyond the scope of the question...

One way or another you have now a class that has a well-defined list of dependencies, that must be specified in the test that attempts to create an instance of this class.

Unit testing becomes easy here:

  SomeService someService = Mockito.mock(SomeService.class);
  MyDao underTest = new MyDao(someService);

Unlike the previous answer given by yuan qingfei, I do not suggest using tools like PowerMock/PowerMockito, consider using those only if you cannot make changes in the code you test (usually its true for old projects that were written without testing in mind)

Mark Bramnik
  • 39,963
  • 4
  • 57
  • 97