Mockito: Comment vérifier que la méthode a été appelée sur un objet créé dans une méthode?
Je suis nouveau à Mockito.
étant donné la classe ci-dessous, Comment puis-je utiliser Mockito pour vérifier que someMethod
a été invoqué exactement une fois après foo
a été invoqué?
public class Foo
{
public void foo(){
Bar bar = new Bar();
bar.someMethod();
}
}
j'aimerais faire l'appel de vérification suivant,
verify(bar, times(1)).someMethod();
où bar
est une instance moquée de Bar
.
7 réponses
si vous injectez l'instance de la barre, ou une usine qui est utilisée pour créer l'instance de la barre (ou l'une des 483 autres façons de le faire), vous aurez l'accès nécessaire pour effectuer le test.
Exemple D'Usine:
donné une classe Foo écrit comme ceci:
public class Foo {
private BarFactory barFactory;
public Foo(BarFactory factory) {
this.barFactory = factory;
}
public void foo() {
Bar bar = this.barFactory.createBar();
bar.someMethod();
}
}
dans votre méthode d'essai vous pouvez injecter un BarFactory comme ceci:
@Test
public void testDoFoo() {
Bar bar = mock(Bar.class);
BarFactory myFactory = new BarFactory() {
public Bar createBar() { return bar;}
};
Foo foo = new Foo(myFactory);
foo.foo();
verify(bar, times(1)).someMethod();
}
Bonus: C'est un exemple de la façon dont TDD peut piloter la conception de votre code.
la réponse classique est," vous ne le faites pas." Vous testez L'API publique de Foo
, pas ses internes.
y a-t-il un comportement de l'objet Foo
(ou, moins bon, un autre objet dans l'environnement) qui est affecté par foo()
? Si oui, test. Et si non, Que fait la méthode?
si vous ne voulez pas utiliser DI ou des usines. Vous pouvez remanier votre classe d'une manière un peu délicate:
public class Foo {
private Bar bar;
public void foo(Bar bar){
this.bar = (bar != null) ? bar : new Bar();
bar.someMethod();
this.bar = null; // for simulating local scope
}
}
et votre classe test:
@RunWith(MockitoJUnitRunner.class)
public class FooTest {
@Mock Bar barMock;
Foo foo;
@Test
public void testFoo() {
foo = new Foo();
foo.foo(barMock);
verify(barMock, times(1)).someMethod();
}
}
alors la classe qui appelle votre méthode foo le fera comme ceci:
public class thirdClass {
public void someOtherMethod() {
Foo myFoo = new Foo();
myFoo.foo(null);
}
}
comme vous pouvez le voir en appelant la méthode de cette façon, vous n'avez pas besoin d'importer la classe Bar dans une autre classe qui appelle votre méthode foo qui est peut-être quelque chose que vous voulez.
bien sûr, l'inconvénient est que vous permettez à l'appelant de régler l'objet de barre.
J'espère que ça aidera.
Solution pour votre exemple de code en utilisant PowerMockito.whenNew
- mockito-tous 1.10.8
- powermock-core 1.6.1
- powermock-module-junit4 1.6.1
- powermock-api-mockito 1.6.1
- junit 4.12
FooTest.java
package foo;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
//Both @PrepareForTest and @RunWith are needed for `whenNew` to work
@RunWith(PowerMockRunner.class)
@PrepareForTest({ Foo.class })
public class FooTest {
// Class Under Test
Foo cut;
@Mock
Bar barMock;
@Before
public void setUp() throws Exception {
cut = new Foo();
}
@After
public void tearDown() {
cut = null;
}
@Test
public void testFoo() throws Exception {
// Setup
PowerMockito.whenNew(Bar.class).withNoArguments()
.thenReturn(this.barMock);
// Test
cut.foo();
// Validations
Mockito.verify(this.barMock, Mockito.times(1)).someMethod();
}
}
je pense que Mockito @InjectMocks
est le chemin à parcourir.
selon votre intention Vous pouvez utiliser:
- injection du constructeur
- propriété setter injection "151980920 Champ" injection
plus d'information dans docs
ci-dessous est un exemple avec injection de champ:
Classes:
public class Foo
{
private Bar bar = new Bar();
public void foo()
{
bar.someMethod();
}
}
public class Bar
{
public void someMethod()
{
//something
}
}
Test:
@RunWith(MockitoJUnitRunner.class)
public class FooTest
{
@Mock
Bar bar;
@InjectMocks
Foo foo;
@Test
public void FooTest()
{
doNothing().when( bar ).someMethod();
foo.foo();
verify(bar, times(1)).someMethod();
}
}
Oui, si vous voulez vraiment / devez le faire, vous pouvez utiliser PowerMock. Cela devrait être considéré comme un dernier recours. Avec PowerMock vous pouvez le faire retourner une moquerie de l'appel au constructeur. Puis faire la vérification sur la moquerie. Cela dit, csturtz est la "bonne" réponse.
voici le lien vers construction simulée de nouveaux objets
une autre façon simple serait d'ajouter une déclaration de log à la barre.someMethod () et ensuite s'assurer que vous pouvez voir ledit message lorsque votre test exécuté, voir les exemples ici: Comment faire une JUnit affirmer sur un message dans un logger
qui est particulièrement pratique quand votre Bar.someMethod () est private
.