53 votes

Se moquer des blocs statiques en Java

Ma devise pour Java est "juste parce que Java a blocs statiques, cela ne signifie pas que vous devriez être en utilisant." Blague à part, il y a beaucoup de trucs en Java qui rendent le test d'un cauchemar. Deux des plus je déteste sont Anonymes, des Classes et des Blocs Statiques. Nous avons beaucoup de code legacy qui rendent l'utilisation de Blocs Statiques et elles sont l'un des ennuyeux points dans nos efforts dans l'écriture de tests unitaires. Notre objectif est d'être capable d'écrire des tests unitaires pour les classes qui dépendent de cette initialisation statique avec un minimum de modifications de code.

Jusqu'à présent, ma suggestion à mes collègues, c'est de déplacer le corps de la statique bloc privée d'une méthode statique et de l'appeler en staticInit. Cette méthode peut alors être appelé à partir de la statique bloc. Pour les tests unitaires d'une autre classe qui dépend de cette classe pourrait facilement se moquer staticInit avec JMockit de ne rien faire. Nous allons voir cela dans l'exemple.

public class ClassWithStaticInit {
  static {
    System.out.println("static initializer.");
  }
}

Sera changé

public class ClassWithStaticInit {
  static {
    staticInit();
  }

  private static void staticInit() {
    System.out.println("static initialized.");
  }
}

Afin que nous puissions effectuer les opérations suivantes dans un JUnit.

public class DependentClassTest {
  public static class MockClassWithStaticInit {
    public static void staticInit() {
    }
  }

  @BeforeClass
  public static void setUpBeforeClass() {
    Mockit.redefineMethods(ClassWithStaticInit.class, MockClassWithStaticInit.class);
  }
}

Cependant, cette solution est également livré avec ses propres problèmes. Vous ne pouvez pas exécuter DependentClassTest et ClassWithStaticInitTest sur la même JVM puisque vous voulez vraiment la statique bloc à exécuter pour ClassWithStaticInitTest.

Quel serait votre façon d'accomplir cette tâche? Ou mieux, non JMockit en fonction des solutions que vous pensez de travail plus propre?

65voto

Jan Kronquist Points 1527

PowerMock est une autre maquette cadre qui s'étend EasyMock et Mockito. Avec PowerMock vous pouvez facilement supprimer un comportement non désiré à partir d'une classe, par exemple un initialiseur statique. Dans votre exemple, vous ajoutez simplement les annotations suivantes à votre cas de test JUnit:

@RunWith(PowerMockRunner.class)
@SuppressStaticInitializationFor("some.package.ClassWithStaticInit")

PowerMock n'utilise pas Java agent et ne nécessite donc pas de modification des paramètres de démarrage de la JVM. Vous ajoutez simplement le fichier jar et les annotations.

14voto

matsev Points 6761

Parfois, je trouve statique initilizers dans les classes que mon code dépend. Si je ne peux pas refactoriser le code, j'utilise PowerMocks' @SuppressStaticInitializationFor d'annotation pour supprimer l'initialiseur statique:

@RunWith(PowerMockRunner.class)
@SuppressStaticInitializationFor("com.example.ClassWithStaticInit")
public class ClassWithStaticInitTest {

    ClassWithStaticInit tested;

    @Before
    public void setUp() {
        tested = new ClassWithStaticInit();
    }

    @Test
    public void testSuppressStaticInitializer() {
        asserNotNull(tested);
    }

    // more tests...
}

Lire plus au sujet de la suppression de comportement indésirable.

Avertissement: PowerMock est un projet open source développé par deux de mes collègues.

13voto

Cem Catikkas Points 4986

Cela va entrer dans plus de JMockit "avancé". Il s'avère que vous pouvez redéfinir les blocs d'initialisation statiques dans JMockit en créant une méthode public void $clinit() . Donc, au lieu de faire ce changement

 public class ClassWithStaticInit {
  static {
    staticInit();
  }

  private static void staticInit() {
    System.out.println("static initialized.");
  }
}
 

nous pourrions aussi bien laisser ClassWithStaticInit tel quel et faire ce qui suit dans MockClassWithStaticInit :

 public static class MockClassWithStaticInit {
  public void $clinit() {
  }
}
 

Cela nous permettra en fait de ne faire aucun changement dans les classes existantes.

6voto

Mike Stone Points 21293

Quand j'ai rencontré ce problème, j'ai l'habitude de faire la même chose que tu décris, sauf que je faire la méthode statique protégé afin que je puisse l'appeler manuellement. En plus de cela, j'ai assurez-vous que la méthode peut être appelée plusieurs fois sans problèmes (sinon c'est pas mieux que l'initialiseur statique autant que les tests).

Cela fonctionne raisonnablement bien, et je peux tester que l'initialiseur statique méthode ne donne ce que j'attends/voulez qu'il fasse. Parfois, il est juste plus facile d'avoir quelques initialisation statique de code, et il n'est tout simplement pas la peine de construire une trop grande complexité du système pour le remplacer.

Lorsque j'utilise ce mécanisme, je fais en sorte de document de la méthode protégée n'est exposée à des fins de test, avec l'espoir qu'il ne sera pas utilisé par d'autres développeurs. Bien sûr, cela peut ne pas être une solution viable, par exemple si la classe de l'interface est visible de l'extérieur (comme un sous-composant d'un certain type pour les autres équipes, ou comme un cadre public). C'est une solution simple au problème, et n'a pas besoin d'une troisième partie de la bibliothèque à mettre en place (que j'aime).

6voto

Justin Standard Points 15312

Me semble que vous êtes le traitement d'un symptôme: une mauvaise conception avec dépendances sur initialisation statique. Peut-être que certains le refactoring est la vraie solution. Il semble que vous avez déjà fait un peu de refactoring avec votre staticInit() de la fonction, mais peut-être que la fonction doit être appelée à partir du constructeur, et non pas à partir d'un initialiseur statique. Si vous pouvez faire disparaître les initialiseurs statiques période, vous serez mieux. Vous seul pouvez prendre cette décision (je ne peux pas voir votre base de code), mais certains refactoring va certainement aider.

Comme pour se moquer, j'utilise EasyMock, mais j'ai couru dans la même question. Effets secondaires des initialiseurs statiques dans le code hérité faire des essais difficiles. Notre réponse a été de refactoriser le initialiseur statique.

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