Mocking with anonymous class in Java

user9608350 :

I'm learning about dependecy injection and testing with Mockito. And I just found a tutorial where someone explain an application with dependency injection and without dependency injection. This is the link: https://www.journaldev.com/2394/java-dependency-injection-design-pattern-example-tutorial

I have 2 questions:

The first question is about the code that he writes for testing. What kind of mock is that? Don't you need to use @Mock to mock an object?

This is his code for testing:

public class MyDIApplicationJUnitTest {

    private MessageServiceInjector injector;
    @Before
    public void setUp(){
        //mock the injector with anonymous class
        injector = new MessageServiceInjector() {

            @Override
            public Consumer getConsumer() {
                //mock the message service
                return new MyDIApplication(new MessageService() {

                    @Override
                    public void sendMessage(String msg, String rec) {
                        System.out.println("Mock Message Service implementation");

                    }
                });
            }
        };
    }

    @Test
    public void test() {
        Consumer consumer = injector.getConsumer();
        consumer.processMessages("Hi Pankaj", "[email protected]");
    }

    @After
    public void tear(){
        injector = null;
    }

} 

And the second question is about testing the app without dependency injection. I don't understand why he say that: "Testing the application will be very difficult since our application is directly creating the email service instance. There is no way we can mock these objects in our test classes." Why we cannot mock these objects in our test cases.

michalk :

The first question is about the code that he writes for testing. What kind of mock is that? Don't you need to use @Mock to mock an object?

To mock an object you have to provide an object that has same type i.e behaves the same or is a subtype of the class that object you want to mock. So to mock an object you can use an instance of anonymous class (in your case it would be an object of anaonymous class that extends MyDIApplication) or you can use Mockito with its @Mock annotation which does basically similiar thing. Bascially using @Mock annotation is similiar to doing :

MyDIApplication myDiApplication = Mockito.mock(MyDIApplication.class)

which creates a mock object extending class passed in constructor. Here you may pass interface or class depending on what you want to mock.

When using anonymous class you provide implementation of methods that you want to mock in overriden implementations of methods, but in case of Mockito you provide intender behaviour by using methods like Mockito::when.

And the second question is about testing the app without dependency injection. I don't understand why he say that: "Testing the application will be very difficult since our application is directly creating the email service instance. There is no way we can mock these objects in our test classes." Why we cannot mock these objects in our test cases.

I guess you are refering to this piece of code :

public class MyApplication {

    private EmailService email = new EmailService();

    public void processMessages(String msg, String rec){
        //do some msg validation, manipulation logic etc
        this.email.sendEmail(msg, rec);
    }
}

Here you create an instance of EmailService as class field. So there is no possibilty you can mock this (although you could use reflection or PowerMock). So you are tightly coupled to EmailService and it is hard to test MyApplication class logic. To be able to test it you can use constructor injection :

public class MyApplication {

    private EmailService email;

    public MyApplication(EmailService emaliService) {
       this.email = emailService;
    }

    public void processMessages(String msg, String rec){
        //do some msg validation, manipulation logic etc
        this.email.sendEmail(msg, rec);
    }
}

Or setter injection :

public class MyApplication {

    private EmailService email;

    public void setEmailService(EmailService emailService) {
        this.email = emailService;
    }

    public void processMessages(String msg, String rec){
        //do some msg validation, manipulation logic etc
        this.email.sendEmail(msg, rec);
    }
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=155742&siteId=1