0
votes

Cas de test Junit pour la méthode privée

J'ai une classe ci-dessous. Je suis nouveau dans l'écriture de tests junit. J'ai besoin d'écrire le cas de test pour cela. Comment puis-je écrire une méthode de test pour la méthode startSchemaMaintenance dans la classe de test car elle appelle une méthode privée sans argument?

public class SchemaMaintenance {

    //Loading statuses overview
    // NOT_STARTED = 0
    // START_LOADING = 1
    // IN_PROGRESS = 2
    // COMPLETED = 3
    // LOADING_ERROR = 4
    private static volatile Integer loading_status = 0;

    public void startSchemaMaintenance() throws Exception {
        if (checkLoadingStatus() == 1) {
            doSchemaMaintenance();

            loading_status = 3;
        }
    }

    private void doSchemaMaintenance(){
        //Do something....
    }

    private int checkLoadingStatus() throws Exception {
        if (loading_status==0 ||loading_status == 2) {
            synchronized (loading_status) {
                if (loading_status==0) {
                    loading_status = 2;
                    return 1;
                }else if(loading_status == 2) {
                    while(loading_status == 2);

                    if((loading_status == 4)){

                        throw new Exception("status = " + 4);
                    }
                }else if(loading_status == 4) {

                    //log.error(generateErrorMessage());
                    throw new Exception("status = " + 4);
                }
            }
        }else if((loading_status == 4)){
            //log.error(generateErrorMessage());
            throw new Exception("status = " + 4 );
        }

        return loading_status;

    }
}


6 commentaires

testez-le en testant la méthode qui l'appelle.


vous voulez dire la méthode qui appelle startSchemaMaintenance


non. Vous comprenez que ce que vous avez posté n'est pas exactement valide Java, non? Vous définissez la valeur de LoadingStatus dans votre test et appelez votre méthode.


pas un java valide ??


La classe Schema Maintenance n'est pas Java valide. Quel est le nom réel de votre fichier?


La meilleure façon de tester une méthode privée n'est pas de la tester directement. sinon, vous pouvez donner un accès au niveau du package à cette méthode en donnant le modificateur par défaut et en écrivant la classe de test dans le même package dans le dossier de test


3 Réponses :


0
votes

Normalement, lorsque vous ne parvenez pas à tester votre code, vous devriez penser à le réécrire, afin qu'il soit testable. Les méthodes privées ne peuvent pas être appelées de manière régulière, en dehors de votre classe, même pas dans un test JUnit.

Il y a plusieurs choses que vous pouvez faire pour le rendre testable:

  1. Réécrivez votre code pour diviser la méthode en plus petites. Doit toujours être fait, le cas échéant.

  2. Vous pouvez rendre le package de méthode privé (supprimer le modificateur privé), afin que votre classe de test, qui doit être dans le même package, puisse y accéder.

  3. Vous pouvez rendre la méthode protégée, ainsi vous pouvez hériter de votre classe dans votre test et appeler indirectement cette méthode sur votre classe héritée.

  4. Vous pouvez appeler une méthode, qui appelle la méthode, que vous souhaitez tester et faire vos affirmations en conséquence.

Si vous avez tendance à changer la visibilité de la méthode en package private ou protected, vous pouvez annoter votre méthode comme @VisibleForTesting , afin que des outils comme Sonar et d'autres membres de l'équipe sachent pourquoi elle n'est pas privée.

/ p>

J'espère que cela aide.


0 commentaires

2
votes

Tout d'abord, deux ou trois choses:

  1. Pourquoi loading_status est-il statique? Fondamentalement, 2 instances de cette classe utiliseraient la même variable loading_status. loading_status ne doit pas être statique ou checkLoadingStatus doit être statique également.
  2. loading_status est terriblement nommé. Si vous voulez le garder statique, vous devez le nommer LOADING_STATUS. Si ce n'est pas statique, appelons-le loadingStatus. C'est la convention Java.
  3. Vous devez créer un type d'énumération approprié pour loading_status au lieu qu'il s'agisse d'un entier.
  4. while (loading_status == IN_PROGRESS); est tout simplement une mauvaise pratique. Au moins ce que vous pouvez faire est: while (loading_status == IN_PROGRESS) {Thread.sleep (100); }; Cependant, un délai d'attente est une meilleure pratique. Quelque chose comme:
src
+-- main
+-- +-- com/app/MyClass.java
+-- test
    +-- com/app/MyClassTest.java // this test class will able to access package-private methods in MyClass

Et enfin je suppose que quelque chose mettra à jour loadingStatus à terminé ou quelque chose. Je suppose que vous utilisez également synchronisé. Si le chargement se produit sur 2 threads différents, vous vous retrouverez dans un dead-lock. Si checkLoadingStatus entrez d'abord le bloc synchronisé, donc lorsque le chargement est terminé, le thread de chargement ne pourra jamais entrer dans le bloc synchronisé, car le checkLoadingStatus maintient le verrou.

Dernier point mais non des moindres pour répondre à votre question , si vous voulez garder votre méthode privée, vous pouvez l'invoquer par réflexion, mais c'est encore une mauvaise pratique. En règle générale, vous devez éviter de tester les méthodes privées et de tester les méthodes qui les invoquent. Si toutefois vous avez vraiment besoin de tester unitaire une méthode particulière, rendez-la privée au lieu de privée, puis vous pouvez créer un test unitaire dans le même package où se trouve la classe qui contient votre méthode. Et vous pouvez ajouter un commentaire à la méthode indiquant qu'elle n'est visible que pour les tests unitaires. Exemple de votre structure de code dans ce cas:

long timeoutMillis = 5000L; // Should come from a configuration or something
long endTimeMillis = System.currentTimeMillis() + timeoutMillis;
while (loadingStatus == IN_PROGRESS) {
    long remainingMillis = end - System.currentTimeMillis();
    if (remaining <= 0) {
        // Timeout while loading
        loadingStatus = LOADING_ERROR;
        break;
    }
    Thread.sleep(50);
}
// ...


2 commentaires

Comment puis-je modifier la variable d'état du chargement tout en testant la méthode StartschemAaintenance ??


Vous ne le faites pas. Vous devez restructurer un peu votre code pour le rendre plus testable. Je ne suis pas un grand partisan du développement piloté par les tests, mais dans votre cas, cela pourrait vous aider à structurer votre code de manière à penser aux cas de test que vous aurez. J'ai écrit ceci dans Notepad ++ et je ne l'ai pas testé, mais vous avez une idée de ce que je veux dire: pastebin.com/uiSydTXU



0
votes

Votre méthode privée fonctionne selon la valeur de la variable privée interne loading_status , donc lors de vos tests, vous devriez simplement pouvoir changer cette variable. Pour ce faire, vous pouvez procéder comme suit:

<dependency>
  <groupId>org.powermock</groupId>
  <artifactId>powermock-api-mockito</artifactId>
  <version>1.6.4</version>
</dependency>

En supposant que vous devez inclure la dépendance mockito sur votre projet:

par exemple Maven

package com.test

import com.test.SchemaMaintenance;
import org.junit.Test;
import org.mockito.internal.util.reflection.Whitebox;

public class SchemaMaintenanceTest {

    @Test
    public void TestSchema() throws Exception {
        SchemaMaintenance schema = new SchemaMaintenance();
        Whitebox.setInternalState(schema,"loading_status",2);
        schema.startSchemaMaintenance();
    }

}


4 commentaires

Il indique qu'aucun nom de champ d'instance loading_status n'a pu être trouvé dans la hiérarchie de classes de SchemaMaintenanceTest lorsque j'ajoute verify (dataBaseSchemaMaintenance, times (0)). DoSchemaMaintenance ();


Pouvez-vous partager tout le code de test unitaire et la sortie d'exception de stacktrace?


De plus, lorsque je débogue la classe SchemaMaintenance, elle n'affiche pas non plus la valeur de loading_status comme 2, elle est toujours 0.


Utilisez-vous le code exact que j'ai fourni dans ma réponse? Je l'ai testé et ça marche. Plus précisément, lorsque je débogue le avec le code que j'ai partagé, la variable loading_status est définie sur 2.