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 que foo
a été appelé?
public class Foo
{
public void foo(){
Bar bar = new Bar();
bar.someMethod();
}
}
Je voudrais faire l'appel de vérification suivant,
verify(bar, times(1)).someMethod();
où bar
est une instance simulée de Bar
.
Si vous injectez l'instance Bar ou une fabrique utilisée pour créer l'instance Bar (ou l'une des 483 autres façons de le faire), vous disposez de l'accès nécessaire pour effectuer le test.
Exemple d'usine:
Étant donné un cours de 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 de test, 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: Ceci est un exemple de la façon dont TDD peut piloter la conception de votre code.
La réponse classique est: "Vous ne faites pas." Vous testez l'API publique de Foo
et non ses internes.
Existe-t-il un comportement de l'objet Foo
(ou moins bon, d'un autre objet de l'environnement) affecté par foo()
? Si oui, testez cela. Et si non, que fait la méthode?
Si vous ne voulez pas utiliser DI ou Factories. Vous pouvez reformer votre classe de manière 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 de 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();
}
}
Ensuite, 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 constater lorsque vous appelez la méthode de cette manière, vous n'avez pas besoin d'importer la classe Bar dans une autre classe qui appelle votre méthode foo, ce qui est peut-être quelque chose que vous souhaitez.
Bien entendu, l'inconvénient est que vous autorisez l'appelant à définir l'objet Bar.
J'espère que ça aide.
Solution pour votre exemple de code en utilisant PowerMockito.whenNew
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 la voie à suivre.
Selon votre intention, vous pouvez utiliser:
Plus d'infos dans docs
Vous trouverez ci-dessous un exemple d'injection sur le terrain:
Des classes:
public class Foo
{
private Bar bar = new Bar();
public void foo()
{
bar.someMethod();
}
}
public class Bar
{
public void someMethod()
{
//something
}
}
Tester:
@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/devez vraiment le faire, vous pouvez utiliser PowerMock. Cela devrait être considéré comme un dernier recours. Avec PowerMock, vous pouvez faire en sorte qu’il renvoie une maquette de l’appel au constructeur. Puis faites la vérification sur la maquette. Cela dit, Csturtz est la "bonne" réponse.
Voici le lien vers Construction fictive de nouveaux objets
Une autre méthode simple consiste à ajouter une instruction de journal à bar.someMethod (), puis à vérifier que vous pouvez voir ledit message lorsque votre test est exécuté. Voir les exemples ici: Comment faire une assertion JUnit sur un message dans un enregistreur
Cela est particulièrement utile lorsque votre Bar.someMethod () est private
.