3

I am practicing with mockito, but I am a bit stuck on how to test a method that depends on a call to method in a local object. See the following example:

public class Worker {          

    public void work() {
                   Vodka vodka = new Vodka();
                   vodka.drink();
     }
}

This worker, instead of doing his job, he likes drinking. But I want to add a test to prove that he drinks while he works. But there is no way of doing so, because I must verify that the method drink() is called when the method work is called. I think you agree with me, that this is impossible to test, so I need to break the dependency before starting to test. Here is my first doubt, what do you think is the best way of breaking such dependency? If I just change the scope of the vodka object to global, I think would not be good(I don't want to expose it to other parts of the class). I thought about creating a factory, something like this:

public class Worker {          

    private VodkaFactory vodkaFactory = new VodkaFactory();


    public void work() {
                   Vodka vodka = vodkaFactory.getVodka();
                   vodka.drink();
     }
}

I am not sure if I did break the dependency correctly, but what I want to do now, is test that the method drink() is called when work() is executed. I tried this with no luck:

@Test
    public void
    does_the_worker_drink_while_working
    () {
        VodkaFactory vodkaFactory = mock(VodkaFactory.class);
        Vodka vodka = mock(Vodka.class);
        Worker worker = new Worker();
        worker.work();
        when(vodkaFactory.getVodka()).thenReturn(vodka);
        verify(vodka,times(1)).drink();
    }

I mock the factory and the when will detect that a new Vodka object is created by the factory. But then when I wan to verify that that method calls 1 time the method drink(), mockito tells me:

Wanted but not invoked:
vodka.drink();
-> at testing_void_methods_from_local_objects.WorkerSpecification.does_the_worker_drink_while_working(WorkerSpecification.java:22)
Actually, there were zero interactions with this mock.

I am not stubbing correctly or I am doing something wrong. Could you give me a hand completing this test and also clarify me what would be the best way of testing such untesteable methods?

I know mockito has a method called, doAnswer() which is used to mock a method call,do you think it can be useful in this case? How should I use it?

UPDATE:

I am following the suggestions to get the when() called before the work() and also I am trying to allow the factory to be set from outside of the class:

@Test
public void
does_the_worker_drink_while_working
() {
    VodkaFactory vodkaFactory = mock(VodkaFactory.class);
    Vodka vodka = mock(Vodka.class);
    Worker worker = new Worker();
    when(vodkaFactory.getVodka()).thenReturn(vodka);
    worker.work();
    verify(vodka,times(1)).drink();
}

This is now the production code now:

public class Worker {          


        private VodkaFactory vodkaFactory;

        public void work() {
                       Vodka vodka = vodkaFactory.getVodka();
                       vodka.drink();
         }

         public void setVodkaFactory(VodkaFactory vodkaFactory) {
               this.vodkaFactory = vodkaFactory;
         }

The exception that I get is the following:

 java.lang.NullPointerException
        at testing_void_methods_called_from_local_objects.Worker.work(Worker.java:9)

This is the line that says vodka.drink()

Sorry by I still confused on what is the problem.

javing
  • 12,307
  • 35
  • 138
  • 211
  • As a few of you mentioned, the first thing I did was call the `when`, before the `work()` method. So I moved up 1 row this line `when(vodkaFactory.getVodka()).thenReturn(vodka);` I run the test but it fails with a null pointer at `vodka.drink();` I am a bit confused with what to do next. I don't want to include The factory to the Constructor of worker, why should I do that? – javing Mar 11 '13 at 23:21
  • Since I recollect seeing another unit-testing question from you, I'd suggest getting a beginner's book which should answer most of the common questions. For this particular question, take a look at this question - http://stackoverflow.com/questions/246038. You shouldn't have to change the method signature to test this.. Instead look at what observable change would happen if Worker drank a Vodka. I'm sure there is one. – Gishu Mar 12 '13 at 07:14
  • Who said it is impossible to test? There are a couple of different mocking tools (PowerMock, JMockit) that you can use to write a unit test for the `work` method, *without* changing it at all. You would simply need to mock the `Vodka` object created internally, which is rather easy to do with either tool, actually. – Rogério Mar 12 '13 at 14:52
  • @Rogerio Can you provide some example of how to test this without breaking the dependencies? PowerMock,JMock and some tool probably let you test private methods and hard dependencies but I don't think those approaches are always desirable. I dont agree with you. – javing Mar 12 '13 at 16:57
  • @sfrj I posted an answer with an example test. Why would something like that not be desirable? I much prefer it to creating unnecessary complexity in the code under test, like a `VodkaFactory` class. – Rogério Mar 12 '13 at 20:16

5 Answers5

3

Your worker creates his own factory class here:

private VodkaFactory vodkaFactory = new VodkaFactory();

The mock you are creating is completely detached from the worker instance and thus the lack of interaction. To make it work, factory has to be injected to worker from "the outside", say via constructor injection.

If this is legacy code, you could use reflection to replace private factory instance with mocked one.

As noted by JB Nizet in comment, your mock setup comes after work is already called. In order to make things right, inject mock and set it up before you call any code utilizing it.

k.m
  • 30,794
  • 10
  • 62
  • 86
  • 2
    +1. Another problem is that the `work()` method is called before the factory has been stubbed, and not after. – JB Nizet Mar 11 '13 at 23:06
  • @JBNizet: thanks, I've incorporated your comment into my answer. – k.m Mar 11 '13 at 23:19
  • @jimmy_keen I think I tried as you guys suggest, but still not working for me(see update). I am confused, with what do you mean by injecting the mock? – javing Mar 11 '13 at 23:44
  • @sfrj: you forgot to inject the mock factory into the Worker: `worker.setVodkaFactory(vodkaFactory)`. I would use a constructor argument rather than a setter to do this. – JB Nizet Mar 12 '13 at 07:57
  • @jimmy_keen Yes I just saw it :) Just fixed it. I knew I was close. Thanks for the help. – javing Mar 12 '13 at 17:00
3

You need to set your vodkaFactory:

@Test
public void
does_the_worker_drink_while_working() {
    VodkaFactory vodkaFactory = mock(VodkaFactory.class);
    Vodka vodka = mock(Vodka.class);
    Worker worker = new Worker();
    when(vodkaFactory.getVodka()).thenReturn(vodka);

    //call your setter        
    worker.setVodkaFactory(vodkaFactory);

    worker.work();
    verify(vodka,times(1)).drink();
}
tallseth
  • 3,635
  • 1
  • 23
  • 24
  • Thanks yes that was the solution. But I will award best answer to jimmi since he answered first with the right answer. +1 – javing Mar 12 '13 at 16:59
1

There is a logical error in the code you are trying to test. Because you have created VodkaFactory instance inside of the Worker class and moreover you have made that field private.

The best solution would be to pass a reference to VodkaFactory from outside of the class.

public class Worker {          

    private VodkaFactory vodkaFactory;

    public void work() {
                   Vodka vodka = vodkaFactory.getVodka();
                   vodka.drink();
     }

     public void setVodkaFactory(VodkaFactory vf) {
           vodkaFactory = vf;
     }

}

Now, in your @Test you can pass your mocked VodkaFactory instance using setVodkaFactory setter.

Lukasz
  • 7,572
  • 4
  • 41
  • 50
1

It is more comment than an answer. In addition to make factory an injectable dependency, you can also make sure to train your mock when(vodkaFactory.getVodka()).thenReturn(vodka); before interacting with it worker.work();

Ievgen Lukash
  • 390
  • 2
  • 7
1

The following is a complete JMockit unit test which exercises the Worker#work() method in isolation from the implementation of its Vodka dependency:

@Test
public void workTest(@Mocked final Vodka mockBeverage)
{
    new Worker().work();

    new Verifications() {{ mockBeverage.drink(); times = 1; }};
}
Rogério
  • 16,171
  • 2
  • 50
  • 63
  • In reply to your comment above. `I much prefer it to creating unnecessary complexity in the code under test` I still dont agree: The initial code that i mentioned was untesteable since had local scope `Vodka vodka = new Vodka();` is calling external logic, and the method under test is depending on the constructor `Vodka()` In this case is simple but the call could be more complex, so I think that is a dependency that should be broken before adding test coverage. If it is not like that, then it would be an integration test, and not a unit test. In any case, thanks for your answer +1 – javing Mar 12 '13 at 22:04
  • I proved that it *is* testable (see test above), and definitely a true unit test. It is no more difficult either, and also applicable to more complex cases. Calling or "depending on" a constructor is not "external logic" or in any way a problem, just regular application code - unless you really want to over-engineer the codebase by demanding the use of patterns such as factories, service locators, or dependency injection even when not called for by actual requirements. – Rogério Mar 13 '13 at 12:47
  • Ok, I will give it a try. What framework is this? I often use Mockito, I see I don't have that `@Mocked` annotation. – javing Mar 13 '13 at 17:25
  • It's JMockit. I added a link to the project site in the answer. You can also use PowerMock, which provides an extension API for Mockito: `whenNew(Vodka.class).withNoArguments().thenReturn(mockBeverage)`. – Rogério Mar 14 '13 at 00:12
  • Thanks, I will have a look. – javing Mar 14 '13 at 10:01