问题
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.
回答1:
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.
回答2:
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();
}
回答3:
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();
回答4:
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.
回答5:
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; }};
}
来源:https://stackoverflow.com/questions/15350239/breaking-a-local-dependency-to-unit-test-a-void-method