web-dev-qa-db-fra.com

Mockito: comment bloquer les méthodes void pour exécuter du code lors de l'appel

Je veux stub une classe de référentiel pour tester une autre classe (classe Holder) qui a un référentiel. L'interface du référentiel prend en charge les opérations CRUD et dispose de nombreuses méthodes, mais mon test unitaire sur la classe Holder n'a besoin que d'en appeler deux. L'interface du référentiel:

public interface IRepo {
    public void remove(String... sarr);

    public void add(String... sarr);

    //Lots of other methods I don't need now
}

Je veux créer une maquette de référentiel qui peut stocker des instances, définir la logique pour add et remove uniquement, et fournir également un moyen de vérifier ce qui est stocké après avoir appelé add et supprimer.

Si je fais:

IRepo repoMock = mock(IRepo.class);

Ensuite, j'ai un objet stupide qui ne fait rien sur chaque méthode. C'est OK, maintenant je dois juste définir le comportement pour ajouter et supprimer.

Je pourrais créer un Set<String> et stub uniquement ces deux méthodes pour travailler sur l'ensemble. Ensuite, j'instancierais un support qui a un IRepo, injecter la maquette partiellement tronquée, et après avoir exercé le support, vérifier l'ensemble pour vérifier qu'il contient ce qu'il devrait.

J'ai réussi à bloquer partiellement une méthode void comme remove en utilisant la méthode obsolète stubVoid:

Set<String> mySet = new HashSet<>();
stubVoid(repoMock).toAnswer(new Answer<Void>() {

    @Override
    public Void answer(InvocationOnMock invocation) throws Throwable {
        Object[] args = invocation.getArguments();
        String[] stringsToDelete = (String[]) args[0];
        mySet.removeAll(Arrays.asList(stringsToDelete));
        return null;
    }
}).on().remove(Matchers.<String>anyVararg());

Mais est obsolète, et ce n'est pas beaucoup mieux que de créer une implémentation partielle pour IRepo. Y a-t-il une meilleure façon?

REMARQUE: Java 7 réponses uniquement s'il vous plaît, cela devrait fonctionner dans Android.

14
Mister Smith

Vous pouvez utiliser

  Mockito.doAnswer(new Answer<Void>() {
        @Override
        public Void answer(InvocationOnMock invocation) throws Throwable {
            //DO SOMETHING
            return null;
        }
    }).when(...).remove(Matchers.<String>anyVararg());

Du Javadoc:

Utilisez doAnswer () lorsque vous souhaitez bloquer une méthode void avec Answer générique.

Le stubbing voids nécessite une approche différente de Mockito.when (Object) car le compilateur n'aime pas les méthodes void entre crochets ...

Exemple:

    doAnswer(new Answer() {
    public Object answer(InvocationOnMock invocation) {
        Object[] args = invocation.getArguments();
        Mock mock = invocation.getMock();
        return null;
    }}).when(mock).someMethod();

Voir des exemples en javadoc pour Mockito

20
Jens

Supposons que vous ayez

public class IFace {
    public void yourMethod() {            
    }
}

Ensuite, pour vous moquer, vous avez besoin

    IFace mock = Mockito.mock(IFace.class);
    Mockito.doAnswer(new Answer() {
        @Override
        public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
            //PUT YOUR CODE HERE
            return null;
        }
    }).when(mock).yourMethod();
3
talex

Si vous n'aimez vraiment pas return null dans votre implémentation de Answer, vous pouvez créer votre propre implémentation Answer qui délègue à une méthode void:

public abstract class DoesSomethingAnswer implements Answer<Void> {

    @Override
    public Void answer(InvocationOnMock invocation) throws Throwable {
        doSomething(invocation);
        return null;
    }

    protected abstract void doSomething(InvocationOnMock invocation);    
}

Ensuite, votre test a une ligne de moins:

    Set<String> mySet = new HashSet<>();
    Mockito.doAnswer(new DoesSomethingAnswer() {
        @Override
        protected void doSomething(InvocationOnMock invocation) {
            Object[] args = invocation.getArguments();
            String[] stringsToDelete = (String[]) args[0];
            mySet.removeAll(Arrays.asList(stringsToDelete));
        }
    }).when(repoMock).remove(Matchers.<String> anyVararg());

Ou, si vous avez juste besoin des arguments de l'invocation:

public abstract class DoesSomethingAnswer implements Answer<Void> {

    @Override
    public Void answer(InvocationOnMock invocation) throws Throwable {
        doSomething(invocation.getArguments());
        return null;
    }

    protected abstract void doSomething(Object[] args);
}
1
Ruben