Question

Il s'agit d'une question générale sur le test d'unité d'une classe Java à l'aide d'objets fictifs.
Je peux résumer mon problème avec cet exemple.
Supposons que j’ai une interface appelée MyInterface.java et un "TwoString". Objet qui ne remplace pas equals ()

"TwoString.java"

   private String string1;
   private String string2;

   public TwoString(String string1, String string2) {
     this.string1 = string1;
     this.string2 = string2;
   }
   ...getters..setters..

"MonInterface.java"

void callMe(TwoString twoString);

Ensuite, j'ai l'objet "MyClass.java" . Son constructeur accepte une implémentation concrète de MyInterface.
MyClass methodToTest () contient la logique permettant de créer un objet TwoString. Disons qu'il sera créé comme

new TwoString("a","b")

Ainsi, lorsque la méthode methodToTest () est appelée, elle crée cet objet TwoString qui sera transmis à la méthode d'interface callMe (TwoString twoString) .

Je veux fondamentalement se moquer de l'interface. Créez un objet MyClass avec cette maquette. Vérifiez ensuite que la méthode fictive est appelée avec une instance spécifique de TwoString.

J'utilise EasyMock et voici du code java

"MyClassTest.java"

public void test() throws Exception {   
   MyInterface myInterfaceMock = createMock(MyInterface.class);
   MyClass myClass = new MyClass(myInterfaceMock);

   myInterfaceMock.callMe(new TwoString("a","b"));   <--- fails here
   expectLastCall();
   replay(myInterfaceMock);

   myClass.methodToTest();
   verify(myInterfaceMock);

Voici le problème. L'objet TwoString que j'attends dans l'appel

myInterfaceMock.callMe(new TwoString("a","b"));

est différent de celui généré dans MyClass.methodToTest () car TwoString.java ne remplace pas égal à égal à.

Je peux ignorer le problème sur l'instance TwoString à l'aide de

myInterfaceMock.callMe((TwoString)anyObject());

mais je veux être sûr que la méthode d'interface est appelée avec une instance spécifique de TwoString qui contient "a" comme chaîne1 et " b " comme string2.

Dans ce cas, l'objet TwoString est très simple et il sera facile de remplacer la méthode equals - mais que faire si j'ai besoin de vérifier un objet plus complexe?

Merci

modifier:

Je vais essayer de le rendre plus lisible avec cet exemple

public class MyClassTest {
    private MyClass myClass;
    private TaskExecutor taskExecutorMock;

    @Before
    public void setUp() throws Exception {
        taskExecutorMock = createMock(TaskExecutor.class);
        myClass = new MyClass(taskExecutorMock);
    }

    @Test
    public void testRun() throws Exception {
        List<MyObj> myObjList = new ArrayList<MyObj>();
        myObjList.add(new MyObj("abc", referenceToSomethingElse));

        taskExecutorMock.execute(new SomeTask(referenceToSomethingElse,  ???new SomeObj("abc", referenceToSomethingElse, "whatever")));   <--- ??? = object created using data in myObjList
        expectLastCall();
        replay(taskExecutorMock);

        myClass.run(myObjList);

        verify(taskExecutorMock);
    }
}

??? SomeObj = objet créé par myClass.run () à l'aide des données contenues dans myObjList.
Disons que SomeObj provient d'une bibliothèque tierce et qu'il ne remplace pas égaux.

Je veux m'assurer que la méthode taskExecutorMock.execute () est appelée avec une instance spécifique de ce SomeObj

Comment puis-je tester que myClass.run () appelle effectivement la méthode taskExecutorMock avec une instance correcte

Était-ce utile?

La solution

Il est possible d'utiliser une méthode de correspondance d'égaux personnalisée à l'aide de org.easymock.IArgumentMatcher .

Cela devrait ressembler à quelque chose comme:

private static <T extends TwoString> T eqTwoString(final TwoString twoString) {
    reportMatcher(new IArgumentMatcher() {
        /** Required to get nice output */
        public void appendTo(StringBuffer buffer) {
            buffer.append("eqTwoString(" + twoString.getString1() + "," + twoString.getString2() + ")");
        }

        /** Implement equals basically */
        public boolean matches(Object object) {
            if (object instanceof TwoString) {
                TwoString other = (TwoString) object;
                // Consider adding null checks below
                return twoString.getString1().equals(object.getString1()) && twoString.getString2().equals(object.getString2());
            }
            else {
                return false;
            }
        }
    });
    return null;
}

Et est utilisé comme suit:

myInterfaceMock.callMe(eqTwoString(new TwoString("a","b")));

Certains détails peuvent ne pas être corrects, mais c'est essentiellement comme je l'ai fait auparavant. Il existe un autre exemple et des explications plus détaillées disponibles dans la documentation EasyMock . Il suffit de rechercher IArgumentMatcher .

Autres conseils

Tout d’abord, vous voulez probablement dire & override equals " Plutôt que d’implémenter, puisque toutes les classes ont une implémentation d’égal (celle qu’elles héritent de Object si elles ne remplacent rien d’autre).

Dans ce cas, la réponse est simple: tous les objets de valeur doivent vraiment implémenter égaux et hashcode. Qu'il s'agisse d'un objet simple comme TwoString ou de l'objet plus complexe auquel vous faites allusion, il devrait incomber à l'objet de vérifier s'il est égal à un autre objet.

La seule autre alternative serait de déconstruire l'objet dans votre code de test - donc au lieu de

assertEquals(expected, twoStr);

vous feriez

assertEquals(expected.getStringOne(), twoStr.getStringOne());
assertEquals(expected.getStringTwo(), twoStr.getStringTwo());

Espérons que vous puissiez voir que cela est mauvais à au moins trois égards. Premièrement, vous dupliquez la logique qui devrait être dans la propre méthode equals () de la classe; et où que vous souhaitiez comparer ces objets, vous devrez écrire le même code.

Deuxièmement, vous ne pouvez voir que l'état public de l'objet. Il est également possible qu'un état privé empêche deux objets apparemment similaires d'être similaires (par exemple, une classe Lift peut avoir un objet accessible au public.) étage "attribut, mais privé" monter ou descendre "également).

Enfin, enfreindre la loi de Demeter est une violation de la loi de Demeter: essayer de déterminer si les choses sont égales.

L'objet lui-même doit implémenter sa propre méthode equals () - pure et simple.

Jetez un coup d’œil à Jakarta Commons Lang: EqualsBuilder.reflectionEquals ()

Bien que je sois d’accord avec dtsazza , tous les objets de valeur devraient avoir une méthode equals () (et hashCode () ), pas toujours approprié pour les tests: la plupart des objets de valeur baseront l’égalité sur une clé plutôt que sur tous les champs.

En même temps, je me méfie de tout test qui veuille vérifier tous les champs: il me semble dire "assurez-vous que cette méthode n'a pas changé quelque chose que je ne prévoyais pas de changer . " Ce qui est un test valide, et à un certain niveau un test très bien intentionné, mais il est un peu effrayant que vous en ressentiez le besoin.

  

Dans ce cas, l'objet TwoString est très simple et il sera facile de remplacer la méthode equals - mais que faire si j'ai besoin de vérifier un objet plus complexe?

Une fois que vos objets commencent à devenir si complexes que vous ne pouvez pas vérifier trivialement s'ils sont égaux d'ailleurs, vous devriez probablement les refactoriser et les injecter en tant que dépendance. Cela changerait le design, mais en général c'est pour le mieux.

Vous semblez également vous appuyer sur certaines connaissances du comportement interne de vos classes. Ce qui précède est un test d’interaction entre deux classes, ce qui fonctionne toujours, mais plus votre ensemble de composants testés est grand, moins vous pouvez vraiment parler de «unité». tests. À un moment donné, vous quittez le domaine des tests unitaires et commencez à effectuer des tests d'intégration. Dans ce cas, il serait peut-être préférable de disposer d'un faisceau de test complet et d'un comportement isolant à certains endroits ...

Vous pouvez y parvenir avec des capteurs d'arguments dans Mockito 1.8.

http: // mockito .googlecode.com / svn / branches / 1.8.0 / javadoc / org / mockito / Mockito.html # 15

Je sais que vous utilisez EasyMock, mais passer à Mockito est simple et beaucoup mieux!

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top