1

Below is the method which I wanted to test but as per my knowledge Junit5 doesn't support PowerMockito. So is there any way I can mock private method call inside another method ?

public Class MyClass {    


private void sendEmailNotification(Checklist Checklist){
    EmailService emailService = new EmailService();
    BaseDTO esDO = newFolderService.getFolderByUri(ServicesUtils.getDecodedCaseNodeUriFromSelfLink(Checklist.getEs_uri()));
    String esName = esDO.getName();
    SharedInfo sharedInfo = Checklist.getShared_info();
    sharedInfo.setEng_space_name(esName);
    String reviewer = Checklist.getReviewer();
    String ChecklistUri = Checklist.getUri();
    String ChecklistName = Checklist.getName();
    String targetPhase = Checklist.getTarget_phase();
    String comment = Checklist.getComment();
    String submitter = Checklist.getSubmitter();
    String appURL = Checklist.getShared_info().getApp_url();
    String ChecklistLink = buildChecklistURL(appURL, ChecklistUri);
    String emailBodyTemplate;
    String emailSubject;

      emailBodyTemplate = EmailTemplates.getEmailTemplateByName(EmailConstants.TEMPLATE_DELIVERABLE_ACCEPTED_REJECTED_WITH_COMMENTS);
      emailSubject = String.format(EmailConstants.ACCEPT_REJECT_WITH_COMMENTS_SUBJECT, ChecklistName, targetPhase);
      emailBodyTemplate = EmailTemplates.replaceSharedVariable(emailBodyTemplate, sharedInfo);
      emailBodyTemplate = EmailTemplates.replaceVariable(emailBodyTemplate, EmailConstants.VAR_TARGET_PHASE, targetPhase);
      emailBodyTemplate = EmailTemplates.replaceVariable(emailBodyTemplate, EmailConstants.VAR_REVIEWER, reviewer);
      emailBodyTemplate = EmailTemplates.replaceVariable(emailBodyTemplate, EmailConstants.VAR_CHECKLIST_ITEM_NAME, ChecklistName);
      emailBodyTemplate = EmailTemplates.replaceVariable(emailBodyTemplate, EmailConstants.VAR_COMMENT, comment);
      emailBodyTemplate = EmailTemplates.replaceVariable(emailBodyTemplate, EmailConstants.VAR_CHECKLIST_ITEM_URL, ChecklistLink);
    try {
      emailService.sendEmail(submitter, EmailConstants.EMAIL_SENDER, emailSubject, emailBodyTemplate);
    } catch (RuntimeException e) {
      Checklist.addError(messages.get(E_ACCEPT_REJECT_SEND_EMAIL));
    }

}

//Method to be tested

public void method(Checklist checklist){

  /*Some Code*/

  sendEmail(checklist);  /* want to ignore this, as throwing NullPointerException*/

  /*Some Code*/

}}
Baljinder
  • 33
  • 1
  • 7
  • Welcome to Stack Overflow. Please take the [tour] to learn how Stack Overflow works and read [ask] on how to improve the quality of your question. Then check the [help/on-topic] to see what questions you can ask. Please see: [Why is “Is it possible to…” a poorly worded question?](https://softwareengineering.meta.stackexchange.com/q/7273). – Progman Jul 10 '20 at 18:26
  • Does this answer your question? [Testing Private method using mockito](https://stackoverflow.com/questions/8799439/testing-private-method-using-mockito) – Pradeep Jul 10 '20 at 19:10

2 Answers2

4

I would suggest to change the scope of the private method to package or protected. You can then override it in a test class that extends your class.

I would not try to mock all the services used in the sendMail method, because your test will then depend on everything within the sendMail method even it doesn't need it. As a result your test must be changed whenever the internals of the sendMail method changes. This would be bad, because your test doesn't need the sendMail method - that's why you want to mock it.

Your test will also get very complex with all the mocks that are only necessary to make the sendMail method work.

A much better approach would be to extract the sendMail method into an interface and create an implementation with the content of the current sendMail method.

public interface ChecklistNotifier {
    public void sendNotification(Checklist checklist);
}

public class EmailChecklistNotifier implements ChecklistNotifier {

   public void sendNotification(Checklist checklist){
    EmailService emailService = new EmailService();
    BaseDTO esDO = newFolderService.getFolderByUri(ServicesUtils.getDecodedCaseNodeUriFromSelfLink(Checklist.getEs_uri()));
    // ...
  }
}

Your client class can then use a ChecklistNotifier.

public class ClientClass {
    
    private ChecklistNotifier notifier;

    public ClientClass(ChecklistNotifier notifier){
         this.notifier = notifier;
    }

    public void method(Checklist checklist){

      /*Some Code*/

      notifier.sendnotification(checklist);  

      /*Some Code*/

    }}
}

Now you can easily create a ClientClass instance in your test and pass it a ChecklistNotifier mock or just a simple implementation.

This approach also honors the SOLID principles, because you have

  • a single responsibility for both the ClientClass and the EmailChecklistNotifier
  • made it open-close - you can replace it with a mock or other implementions, maybe SMS notification
  • a segregated interface - ChecklistNotification
  • inversed the dependency and therfore decoupled the ClientClass from the mail sending implementation dependencies.
René Link
  • 48,224
  • 13
  • 108
  • 140
3

You are correct. Powermock does not yet support JUnit 5 and there is an open issue in their official github repository here.

There doesnt seem to be any easy way to mock private methods using Junit5 runner , unless of course you decide to use a custom classloader and do bytecode manipulation.

However, instead of mocking the whole method, I would suggest you to mock the dependency which is used to send the email (unless that dependency uses some final method).

And if you can't even do that, then best way is to use Junit4 instead of Junit5.

Abhinaba Chakraborty
  • 3,488
  • 2
  • 16
  • 37
  • you can use Spy to mock method if you want – Pradeep Jul 10 '20 at 19:04
  • But as it is private you can change it to protected so you can access it in test package – Pradeep Jul 10 '20 at 19:06
  • I cant change class methods to protected for some reason, I heard about ReflectionUtils from Junit or Spring, but not sure if that can help, in this case as i just want this method to do nothing , any clue regarding if this is possible in any way ? – Baljinder Jul 10 '20 at 19:39
  • @Baljinder ReflectionUtils is not for mocking. It is a simple utility class for working with the reflection API and handling reflection exceptions. **Did you try mocking the dependency which is used to send the email** (as I suggested in my answer)? Can you show us that **sendEmail** method? – Abhinaba Chakraborty Jul 11 '20 at 05:13
  • 1
    Hello @AbhinabaChakraborty , Please find the send Email method. It worked after mocking emailService , Thanks for the help – Baljinder Jul 12 '20 at 05:02
  • 1
    I have the same problem. I would like to mock a private method with Junit 5 :( – Gabriel García Garrido Jan 17 '22 at 08:38