web-dev-qa-db-fra.com

Pourquoi mon test unitaire lié à JSONObject échoue-t-il?

J'exécute mes tests en utilisant gradle testFlavorType

JSONObject jsonObject1 = new JSONObject();
JSONObject jsonObject2 = new JSONObject();
jsonObject1.put("test", "test");
jsonObject2.put("test", "test");
assertEquals(jsonObject1.get("test"), jsonObject2.get("test"));

Le test ci-dessus réussit.

jsonObject = new SlackMessageRequest(channel, message).buildBody();
String channelAssertion = jsonObject.getString(SlackMessageRequest.JSON_KEY_CHANNEL);
String messageAssertion = jsonObject.getString(SlackMessageRequest.JSON_KEY_TEXT);
assertEquals(channel, channelAssertion);
assertEquals(message, messageAssertion);

Mais les deux demandes ci-dessus échouent. La trace de pile indique que channelAssertion et messageAssertion sont nuls, mais ne savent pas pourquoi. Ma question est: Pourquoi les deux assertions ci-dessus échouent-elles?

Ci-dessous se trouve le SlackMessageRequest.

public class SlackMessageRequest
        extends BaseRequest {
    // region Variables

    public static final String JSON_KEY_TEXT = "text";
    public static final String JSON_KEY_CHANNEL = "channel";

    private String mChannel;
    private String mMessage;

    // endregion

    // region Constructors

    public SlackMessageRequest(String channel, String message) {
        mChannel = channel;
        mMessage = message;
    }

    // endregion

    // region Methods

    @Override
    public MethodType getMethodType() {
        return MethodType.POST;
    }    

    @Override
    public JSONObject buildBody() throws JSONException {
        JSONObject body = new JSONObject();
        body.put(JSON_KEY_TEXT, getMessage());
        body.put(JSON_KEY_CHANNEL, getChannel());
        return body;
    }

    @Override
    public String getUrl() {
        return "http://localhost:1337";
    }

    public String getMessage() {
        return mMessage;
    }

    public String getChannel() {
        return mChannel;
    }

// endregion
}

Voici la trace de pile:

junit.framework.ComparisonFailure: expected:<@tk> but was:<null>
    at junit.framework.Assert.assertEquals(Assert.Java:100)
    at junit.framework.Assert.assertEquals(Assert.Java:107)
    at junit.framework.TestCase.assertEquals(TestCase.Java:269)
    at com.example.app.http.request.SlackMessageRequestTest.testBuildBody(SlackMessageRequestTest.Java:30)
    at Sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at Sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.Java:62)
    at Sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.Java:43)
    at Java.lang.reflect.Method.invoke(Method.Java:483)
    at junit.framework.TestCase.runTest(TestCase.Java:176)
    at junit.framework.TestCase.runBare(TestCase.Java:141)
    at junit.framework.TestResult$1.protect(TestResult.Java:122)
    at junit.framework.TestResult.runProtected(TestResult.Java:142)
    at junit.framework.TestResult.run(TestResult.Java:125)
    at junit.framework.TestCase.run(TestCase.Java:129)
    at junit.framework.TestSuite.runTest(TestSuite.Java:252)
    at junit.framework.TestSuite.run(TestSuite.Java:247)
    at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.Java:86)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.Java:86)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.Java:49)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.Java:64)
    at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.Java:50)
    at Sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at Sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.Java:62)
    at Sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.Java:43)
    at Java.lang.reflect.Method.invoke(Method.Java:483)
    at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.Java:35)
    at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.Java:24)
    at org.gradle.messaging.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.Java:32)
    at org.gradle.messaging.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.Java:93)
    at com.Sun.proxy.$Proxy2.processTestClass(Unknown Source)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.Java:106)
    at Sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at Sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.Java:62)
    at Sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.Java:43)
    at Java.lang.reflect.Method.invoke(Method.Java:483)
    at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.Java:35)
    at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.Java:24)
    at org.gradle.messaging.remote.internal.hub.MessageHub$Handler.run(MessageHub.Java:360)
    at org.gradle.internal.concurrent.DefaultExecutorFactory$StoppableExecutorImpl$1.run(DefaultExecutorFactory.Java:64)
    at Java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.Java:1142)
    at Java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.Java:617)
    at Java.lang.Thread.run(Thread.Java:745)

MODIFIER 17H55 HNE

J'ai compris que je peux me connecter avec System.out.println("") puis voir les résultats en exécutant gradle testFlavorType --debug Et par essais et erreurs, j'ai découvert la situation bizarre suivante:

@Override
public JSONObject buildBody() throws JSONException {
    System.out.println("buildBody mChannel = " + mChannel);
    System.out.println("buildBody mMessage = " + mMessage);
    JSONObject body = new JSONObject();
    body.put(JSON_KEY_TEXT, getMessage());
    body.put(JSON_KEY_CHANNEL, getChannel());

    if (body.length() != 0) {
        Iterator<String> keys = body.keys();

        if (keys.hasNext()) {
            do {
                String key = keys.next();
                System.out.println("keys: " + key);
            } while (keys.hasNext());
        }
    } else {
        System.out.println("There are no keys????");
    }

    return body;
}

Pour une raison quelconque, "Il n'y a pas de clés ????" imprime-t-il?!?!?!?!! Pourquoi?!

MODIFIER 18H20 HNE

J'ai compris comment déboguer les tests unitaires. Selon le débogueur, le JSONObject affecté renvoie "null". Je n'ai aucune idée de ce que cela signifie (voir ci-dessous). Étant donné que je pense que cela est pertinent, mon dossier de fin d'études comprend les éléments suivants:

testOptions {
    unitTests.returnDefaultValues = true
}

C'est particulièrement étrange parce que si je construis un JSONObject à l'intérieur du test, alors tout fonctionne bien. Mais si cela fait partie du code de l'application d'origine, cela ne fonctionne pas et fait ce qui précède.

enter image description here

35
tambykojak

La classe JSONObject fait partie du SDK Android. Cela signifie que ce n'est pas disponible par défaut pour les tests unitaires.

De http://tools.Android.com/tech-docs/unit-testing-support

Le fichier Android.jar utilisé pour exécuter les tests unitaires ne contient aucun code réel - fourni par l'image système Android sur les appareils réels. Au lieu de cela, toutes les méthodes génèrent des exceptions (par défaut) C'est pour vous assurer que vos tests unitaires ne testent que votre code et ne dépendent d'aucun comportement particulier de la plate-forme Android (que vous n'avez pas explicitement moqué, par exemple en utilisant Mockito).

Lorsque vous définissez les options de test sur

testOptions {
    unitTests.returnDefaultValues = true
}

vous corrigez la "méthode ... pas moquée". problème, mais le résultat est que lorsque votre code utilise new JSONObject() vous n'utilisez pas la vraie méthode, vous utilisez une méthode fictive qui ne fait rien, elle retourne juste une valeur par défaut. C'est la raison pour laquelle l'objet est null.

Vous pouvez trouver différentes façons de résoudre le problème dans cette question: les méthodes Android ne sont pas moquées lors de l'utilisation de Mockito

45
Lucas L.

Comme le dit Lucas, JSON est fourni avec le Android SDK, donc vous travaillez avec un stub.

La solution actuelle consiste à extraire JSON de Maven Central comme ceci:

dependencies {
    ...
    testCompile 'org.json:json:20180130'
}

Alternativement, vous pouvez télécharger et inclure le pot:

dependencies {
    ...
    testCompile files('libs/json.jar')
}

C'est inconn quelle version de l'artefact maven correspond exactement/le plus étroitement à ce qui est livré avec Android.

Notez que vous devez également utiliser Android Studio 1.1 ou supérieur et au moins créer des outils version 22.0.0 ou supérieure pour que cela fonctionne.

Problème connexe: # 179461

51
David Miguel

Eh bien, mon premier pressentiment serait que votre méthode getMessage() renvoie null. Vous pouvez montrer le corps de cette méthode dans votre question et nous demander de trouver la réponse pour vous, mais vous devriez probablement rechercher comment déboguer les applications Android utilisant des points d'arrêt).
De cette façon, vous pouvez exécuter votre code étape par étape et voir les valeurs de chaque variable à chaque étape. Cela vous montrerait votre problème en un rien de temps, et c'est une compétence que vous devez absolument maîtriser dès que possible si vous avez l'intention de vous impliquer sérieusement dans la programmation.

0
Zoltán