101 votes

Se moquant de l'énumération Java pour ajouter une valeur pour tester le cas d'échec.

J'ai un enum switch plus ou moins comme ceci:

public static enum MyEnum {A, B}

public int foo(MyEnum value) {
    switch(value) {
        case(A): return calculateSomething();
        case(B): return calculateSomethingElse();
    }
    throw new IllegalArgumentException("Ne sais pas comment gérer " + value);
}

et j'aimerais que toutes les lignes soient couvertes par les tests, mais comme le code est censé traiter toutes les possibilités, je ne peux pas fournir une valeur sans son instruction de cas correspondante dans le switch.

Étendre l'enum pour ajouter une valeur supplémentaire n'est pas possible, et simplement simuler la méthode equals pour renvoyer false ne fonctionnera pas non plus car le bytecode généré utilise une table de saut en coulisses pour accéder au bon cas... J'ai donc pensé que peut-être une sorte de magie noire pourrait être réalisée avec PowerMock ou quelque chose.

Merci!

edit:

Comme je possède l'énumération, j'ai pensé que je pourrais simplement ajouter une méthode aux valeurs et ainsi éviter complètement le problème du switch; mais je laisse la question telle quelle car elle reste intéressante.

2voto

Kevin Peterson Points 4456

JMock (au moins à partir de la version 2.5.1 que j'utilise) peut le faire directement. Vous aurez besoin de définir votre Mockery pour utiliser ClassImposterizer.

Mockery mockery = new Mockery();
mockery.setImposterizer(ClassImposterizer.INSTANCE);
MyEnum unexpectedValue = mockery.mock(MyEnum.class);

0voto

Jin Thakur Points 553

Tout d'abord, Mockito peut créer des données factices qui peuvent être des entiers longs, etc. Il ne peut pas créer une énumération correcte car une énumération a un nombre spécifique d'ordinal, de nom, de valeur, etc. Donc, si j'ai une énumération

public enum HttpMethod {
      GET, POST, PUT, DELETE, HEAD, PATCH;
}

j'ai donc un total de 5 ordinaux dans l'énumération HttpMethod mais Mockito ne le sait pas. Mockito crée des données factices et c'est toujours null, donc vous finirez par passer une valeur nulle. Voici donc la solution proposée : vous randomisez l'ordinal et obtenez une bonne énumération qui peut être passée pour d'autres tests

import static org.mockito.Mockito.mock;

import java.util.Random;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Matchers;
import org.mockito.internal.util.reflection.Whitebox;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import com.amazonaws.HttpMethod;

//@Test(expected = {"LoadableBuilderTestGroup"})
//@RunWith(PowerMockRunner.class)
public class testjava {
   // private static final Class HttpMethod.getClass() = null;
    private HttpMethod mockEnumerable;

    @Test
    public void setUpallpossible_value_of_enum () {
        for ( int i=0 ;i<10;i++){
            String name;
            mockEnumerable= Matchers.any(HttpMethod.class);
            if(mockEnumerable!= null){
                System.out.println(mockEnumerable.ordinal());
                System.out.println(mockEnumerable.name());

                System.out.println(mockEnumerable.name()+"mocking suceess");
            }
            else {
                //Randomize all possible value of enum 
                Random rand = new Random();
                int ordinal = rand.nextInt(HttpMethod.values().length); 
                // 0-9. mockEnumerable=
                mockEnumerable= HttpMethod.values()[ordinal];
                System.out.println(mockEnumerable.ordinal());
                System.out.println(mockEnumerable.name());
            }
        }
    }

    @Test
    public void setUpallpossible_value_of_enumwithintany () {
        for ( int i=0 ;i<10;i++){
            String name;
            mockEnumerable= Matchers.any(HttpMethod.class);
            if(mockEnumerable!= null){
                System.out.println(mockEnumerable.ordinal());
                System.out.println(mockEnumerable.name());

                System.out.println(mockEnumerable.name()+"mocking suceess");
            } else {
               int ordinal;
               //Randomize all possible value of enum 
               Random rand = new Random();
               int imatch = Matchers.anyInt();
               if(  imatch>HttpMethod.values().length)
                 ordinal = 0    ;
               else
                ordinal = rand.nextInt(HttpMethod.values().length);

               // 0-9.  mockEnumerable=
               mockEnumerable= HttpMethod.values()[ordinal];
               System.out.println(mockEnumerable.ordinal());
               System.out.println(mockEnumerable.name());       
            }
       }  
    }
}

Sortie :

0
GET
0
GET
5
PATCH
5
PATCH
4
HEAD
5
PATCH
3
DELETE
0
GET
4
HEAD
2
PUT

0voto

Steven Kuypers Points 1

Je pense que le moyen le plus simple d'obtenir l'IllegalArgumentException est de passer null à la méthode foo et vous lirez "Ne sais pas comment gérer null"

-1voto

Ryan Points 11

J'ai ajouté une option Inconnue à mon énumération, que je passe pendant le test. Pas idéal dans tous les cas, mais simple.

-11voto

eliocs Points 3607

Je mettrais le cas par défaut avec l'un des cas d'énumération :

  public static enum MyEnum {A, B}

  public int foo(MyEnum value) {
    if (value == null) throw new IllegalArgumentException("Ne sais pas comment traiter " + value);

    switch(value) {
        case(A):
           return calculateSomething();
        case(B):
        default:
           return calculateSomethingElse();
    }
  }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X