Question

Je dois simuler un scénario de test dans lequel j'appelle la méthode d'un objet getBytes() à cordes et je reçois un UnsupportedEncodingException.

J'ai essayé d'y parvenir en utilisant le code suivant:

String nonEncodedString = mock(String.class);
when(nonEncodedString.getBytes(anyString())).thenThrow(new UnsupportedEncodingException("Parsing error."));

Le problème est que lorsque je lance mon cas de test je reçois un MockitoException qui dit que je ne peux pas se moquer d'une classe java.lang.String.

Y at-il un moyen de se moquer d'un objet String à l'aide Mockito ou, alternativement, un moyen de faire mon objet String jeter un UnsupportedEncodingException quand je l'appelle la méthode getBytes?


Voici plus de détails pour illustrer le problème:

Ceci est la classe que je veux tester:

public final class A {
    public static String f(String str){
        try {
            return new String(str.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            // This is the catch block that I want to exercise.
            ...
        }
    }
}

Ceci est ma classe de test (je JUnit 4 et Mockito):

public class TestA {

    @Test(expected=UnsupportedEncodingException.class)
    public void test(){
        String aString = mock(String.class);
        when(nonEncodedString.getBytes(anyString())).thenThrow(new UnsupportedEncodingException("Parsing error."));
        A.f(aString);
    }
}
Était-ce utile?

La solution

Le problème est la classe en Java String est marquée comme finale, de sorte que vous ne pouvez pas se moquer utilise des cadres moqueurs traditionnels. Selon le , ceci est une limitation de ce cadre ainsi.

Autres conseils

Que diriez-vous simplement créer un avec un mauvais String nom de l'encodage? Voir

public String(byte bytes[], int offset, int length, String charsetName)

Mocking est presque certainement <=> une mauvaise idée.

Si tout ce que vous allez faire dans votre bloc catch est de lancer une exception d'exécution, vous pouvez vous économiser quelques secondes en utilisant simplement un objet Charset pour spécifier le nom définir votre caractère.

public final class A{
    public static String f(String str){
        return new String(str.getBytes(Charset.forName("UTF-8")));
    }
}

De cette façon, vous n'êtes pas attrapez une exception qui ne se produira jamais simplement parce que le compilateur vous indique.

De la documentation, Jdave ne peut pas supprimer les modificateurs « finale » de classes chargées par le chargeur de classes d'amorçage. Cela inclut toutes les classes JRE (de java.lang, java.util, etc.).

Un outil qui ne vous laisse moquez tout est JMockit .

Avec JMockit, votre test peut être écrit:

import java.io.*;
import org.junit.*;
import mockit.*;

public final class ATest
{
   @Test(expected = UnsupportedOperationException.class)
   public void test() throws Exception
   {
      new Expectations()
      {
         @Mocked("getBytes")
         String aString;

         {
            aString.getBytes(anyString);
            result = new UnsupportedEncodingException("Parsing error.");
         }
      };

      A.f("test");
   }
}

en supposant que la classe "A" complet est:

import java.io.*;

public final class A
{
   public static String f(String str)
   {
      try {
         return new String(str.getBytes("UTF-8"));
      }
      catch (UnsupportedEncodingException e) {
         throw new UnsupportedOperationException(e);
      }
   }
}

En fait, j'exécuté ce test dans ma machine. (Remarquez que j'Enveloppez la exception vérifiée d'origine dans une exception d'exécution).

je mock partiel pour éviter par JMockit @Mocked("getBytes") de se moquant tout dans la classe java.lang.String (imaginez ce qui pourrait causer).

Maintenant, ce test est vraiment inutile, parce que « UTF-8 » est un jeu de caractères standard, qui doit être pris en charge dans tous les JREs. Par conséquent, dans un environnement de production, le bloc catch ne sera jamais exécutée.

Le « besoin » ou le désir de couvrir le bloc catch est toujours valide, cependant. Alors, comment se débarrasser de l'essai sans réduire le pourcentage de couverture? Voici mon idée: insérer une ligne avec comme première assert false; instruction à l'intérieur du bloc catch, et que l'outil de couverture de code ignorer tout le bloc catch lors de la déclaration des mesures de couverture. C'est l'un de mes « TODOs » pour JMockit couverture. 8 ^)

Mockito ne peut pas se moquer des classes finales. JMock, associé à une bibliothèque de Jdave peut. Voici les instructions .

JMock ne fait pas quelque chose de spécial pour les classes finales autres que de compter sur la bibliothèque Jdave Définalisation tout dans la machine virtuelle Java, de sorte que vous pouvez expérimenter l'utilisation de la unfinalizer de Jdave et voir si Mockito se moquera alors il.

Vous pouvez également utiliser l'extension Mockito de PowerMock pour se moquer de classes / méthodes finales, même dans les classes de système telles que String. Cependant, je voudrais aussi un avis déconseillant moqueur getBytes dans ce cas et plutôt essayer de configurer votre attente afin qu'un réel est une chaîne remplie avec les données attendues est utilisé à la place.

Vous allez tester le code qui ne peut jamais être exécuté. support UTF-8 est nécessaire pour être dans chaque machine virtuelle Java, voir http://java.sun.com/javase/6/docs/api/java/nio/charset/Charset.html

  

Il est une exigence de projet que les tests unitaires pourcentage de couverture doit mais supérieure à une valeur donnée. Pour atteindre un tel pourcentage de couverture des tests doivent couvrir le bloc de capture par rapport au UnsupportedEncodingException.

Qu'est-ce que cet objectif de couverture donnée? Certaines personnes disent que tir pour une couverture à 100% ne sont pas toujours bonne idée .

D'ailleurs, ce n'est pas moyen de vérifier si oui ou non un bloc de capture a été exercé. La bonne façon est d'écrire une méthode qui fait l'exception à levée et faire l'observation de l'exception étant jeté le critère de réussite. Pour ce faire, avec l'annotation @Test JUnit en ajoutant la valeur « attendue »:

@Test(expected=IndexOutOfBoundsException.class) public void outOfBounds() {
   new ArrayList<Object>().get(1);
}

Avez-vous essayé le passage d'un charsetName invalide à getBytes (String)?

Vous pouvez mettre en œuvre une méthode d'aide pour obtenir le charsetName, et remplacer cette méthode au sein de votre test à une valeur non-sens.

Peut-être A.f (String) doit être A.f (CharSequence) à la place. Vous pouvez se moquer d'un CharSequence.

Si vous pouvez utiliser JMockit, regardez réponse Rogério.

Si et seulement si votre objectif est d'obtenir une couverture de code, mais pas simulent en fait ce qui manque UTF-8 ressemblerait à l'exécution, vous pouvez effectuer les opérations suivantes (et que vous ne pouvez pas ou ne voulez pas utiliser JMockit):

public static String f(String str){
    return f(str, "UTF-8");
}

// package private for example
static String f(String str, String charsetName){
    try {
        return new String(str.getBytes(charsetName));
    } catch (UnsupportedEncodingException e) {
        throw new IllegalArgumentException("Unsupported encoding: " + charsetName, e);
    }
}

public class TestA {

    @Test(expected=IllegalArgumentException.class)
    public void testInvalid(){
        A.f(str, "This is not the encoding you are looking for!");
    }

    @Test
    public void testNormal(){
        // TODO do the normal tests with the method taking only 1 parameter
    }
}

Vous pouvez changer votre méthode pour prendre l'interface CharSequence:

public final class A {
    public static String f(CharSequence str){
        try {
            return new String(str.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            // This is the catch block that I want to exercise.
            ...
        }
    }
}

De cette façon, vous pouvez toujours passer dans la chaîne, mais vous pouvez se moquer de quelque façon que vous le souhaitez.

Si vous avez un bloc de code qui ne peut jamais réellement être exécuté, et une exigence de gestion d'avoir une couverture de test de 100%, alors quelque chose va devoir changer.

Ce que vous pouvez faire est de rendre le caractère codant pour une variable membre, et ajoutez un constructeur de package-privé à votre classe qui vous permet de le passer. Dans votre test unitaire, vous pouvez appeler le nouveau constructeur, avec une valeur non-sens pour le codage de caractères.

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