12
votes

Paramètre de la méthode de capture dans JMOCK pour passer à une implémentation encastrée

Je souhaite atteindre le comportement suivant. Ma classe sous test a une dépendance à une autre classe, je souhaite se moquer de cette dépendance avec JMock. La plupart des méthodes rendraient des valeurs standard, mais il y a une méthode, où je souhaite appeler à une implémentation stupéfiante, je sais que je peux appeler cette méthode à partir du (...) code> Mais je veux que la méthode soit appelée par les mêmes paramètres passés à la méthode moquée.

Test p> xxx pré>

interface p> xxx pré>

méthode à appeler p> xxx Pré>

Alors, comment capturer le paramètre qui a été transmis à la méthode étant moquée, de sorte que je puisse les transmettre à la méthode surbecked à la place? p>

EDIT PART> P >

Juste pour donner un autre exemple, disons que je souhaite me moquer de la dépendance code> inact> dans le code suivant (C #) pour tester le haut-parleur de classe P>

public class Speaker
{
  private readonly string firstName;
  private readonly string surname;
  private INameSource nameSource ;
 public Speaker(string firstName, string surname, INameSource nameSource)
  {
    this.firstName = firstName;
    this.surname = surname;
    this.nameSource = nameSource;
  }
  public string Introduce()
  {
    string name = nameSource.CreateName(firstName, surname);
    return string.Format("Hi, my name is {0}", name);
  }
}
public interface INameSource
{
  string CreateName(string firstName, string surname);
}


0 commentaires

3 Réponses :


0
votes

JMOCK ne prend pas en charge votre cas d'utilisation (ou tout autre cadre moqueur que je connaisse dans Java).

Il y a une petite voix dans ma tête qui dit que ce que vous essayez de faire n'est pas idéal et que votre test de l'unité pourrait être compliqué (peut-être que cela teste trop de code / logique?). L'un des problèmes que je vois, c'est que vous ne savez pas quelles valeurs qui se moquent de retourner et vous branchez autre chose, ce qui pourrait faire chaque point irréproductible.


5 commentaires

Je ne pense pas que je teste trop, disons que cette méthode vient de multiplier le numéro transduit avec un facteur. Ce facteur est interne à la dépendance (peut-il s'agir d'un fichier de configuration). Donc, afin d'éviter de lire le fichier de configuration (ou quoi que ce soit, cela le fait), je voudrais vous moquer de la méthode par le fait de dire que le retour renvoie toujours 3 fois le nombre passé. Je sais que cela peut ne pas être le meilleur exemple. J'ai fait des tests similaires en C # avec des moqueurs de rhinocéros.


Votre exemple a une faille de conception: les paramètres de lecture d'un fichier de configuration et calculer une valeur sont 2 choses différentes à effectuer par différentes classes. Je ne sais pas à quel point vous êtes familier avec Inversion de dépendance , mais je vous suggérerait de lire un peu à ce sujet.


oui je suis conscient de ces techniques. C'est comment j'injecte la dépendance que je veux me moquer


Merci d'avoir souligné la faille de conception. Mais c'est juste un exemple, je suis si je peux ajouter mon cas d'utilisation réelle ici


Vous pouvez vérifier la question modifiée pour un étui d'utilisation éventuellement valide et la façon dont Rhino Framework en C # soutient que



1
votes

Comme Augusto, je ne suis pas convaincu que c'est une bonne idée en général. Cependant, je ne pouvais pas résister à avoir un petit jeu. J'ai créé un correspondant personnalisé et une action personnalisée qui stocke et renvoie l'argument fourni.

Remarque: Ceci est loin du code prêt à la production; Je viens de m'amuser. Voici un test d'unité autonome qui prouve la solution: p>

@RunWith(JMock.class)
public class ExampleTest {

  private Mockery context = new JUnit4Mockery();

  @Test
  public void testFormalName() {
    // I would separately test implementations of NameSource
    Assert.assertEquals("Joe Bloggs", new Formal().createName("Joe", "Bloggs"));
  }

  @Test
  public void testSpeaker() {
    // I would then test only the important features of Speaker, namely
    // that it passes the right values to the NameSource and uses the
    // response correctly
    final NameSource nameSource = context.mock(NameSource.class);
    final String firstName = "Foo";
    final String lastName = "Bar";
    final String response = "Blah";

    context.checking(new Expectations() {
      {
        // We expect one invocation with the correct params
        oneOf(nameSource).createName(firstName, lastName);
        // We don't care what it returns, we just need to know it
        will(returnValue(response));
      }
    });

    Assert.assertEquals(String.format("Hi, my name is %s", response),
        new Speaker(firstName, lastName, nameSource).introduce());
  }
}


4 commentaires

Nice ... Je pense que je peux généraliser ceci ... vient de passer de C # à Java récemment, je ne connaissais pas les matchers et actions personnalisés. J'ai mis à jour la question à montrer comment il est pris en charge dans des moqueurs de rhinocéros pour C # et des liens qui donne son utilisation


@Jugalthakkar Je suppose que vous vous êtes notifié sur les modifications apportées aux réponses, mais je pensais que je ferais mieux de vous ping de toute façon. Voir ci-dessus pour un moyen de résoudre votre problème sans avoir recours à tout ce que nous considérerons étrange .


Merci pour la réponse mise à jour, je vois l'erreur que je vous engageais, puis-je essayer de (ou du moins finalement) tester deux choses à la fois. Je ne peux pas croire comment je l'ai manqué. Mon cas de véritable usage est un peu plus complexe que l'exemple, mais maintenant je pense que je suis sur le bon chemin. Quoi qu'il en soit, l'objectif principal était de trouver une alternative à l'action qui vous permet de passer du délégué alternatif pour un appel donné d'une méthode donnée d'un objet de simulation. Le match de stockage m'a aidé à mieux comprendre la façon dont JMock fonctionne


@Jugalthakkar amende, mais gardez à l'esprit qu'il n'est pas normal de faire ce que j'ai suggéré au début de ma réponse. Je suis sûr qu'il y a toujours une alternative. N'hésitez pas à poser une autre question à l'avenir si vous vous trouvez trop violemment JMOCK.



22
votes

La solution de Duncan fonctionne bien, mais il existe même une solution plus simple sans recourir à un correspondant personnalisé. Utilisez simplement l'argument d'invocation transmis à la méthode d'invoquer des personnalités. Dans cet argument, vous pouvez appeler la méthode GetParameter (Long I) qui vous donne la valeur de l'appel.

donc au lieu de ce p> xxx pré>

Utilisez ceci P>

@RunWith(JMock.class)
public class Example {

  private Mockery context = new JUnit4Mockery();

  @Test
  public void Test() {

    final IDependency mockObject = context.mock(IDependency.class);

    context.checking(new Expectations() {
      {
        // No custom matcher required here
        allowing(mockObject).methodToInvoke(with(any(Integer.class)));

        // The action will return the first argument of the method invocation.
        will(new CustomAction("returns first arg") {
          @Override
          public Object invoke(Invocation invocation) throws Throwable {
            return (Integer) invocation.getParameter(0);
          }
        });
      }
    });

    Integer test1 = 1;
    Integer test2 = 1;

    // Confirm the object passed to the mocked method is returned
    Assert.assertEquals((Object) test1, mockObject.methodToInvoke(test1));
    Assert.assertEquals((Object) test2, mockObject.methodToInvoke(test2));
  }

  public interface IDependency {
    public int methodToInvoke(int arg);
  }


3 commentaires

Cool quelque chose de très semblable à ce que j'ai fait dans C #. Je peux maintenant simplement écrire simplement Retour StubmetTobeBeuVokedInstead ((Integer) Invocation.getParameter (0)); sans aucun correspondeur personnalisé ou quoi que ce soit, une action personnalisée inline anonyme ??? !!! ... à Lambdas dans ce cas.


Oui! Ça y est. J'ai réussi à utiliser cette stratégie pour remplacer partiellement la valeur renvoyée par défaut. J'utilisais la méthode (R) pour simuler des méthodes similaires avec différents types de retour (Boolean et Void), mais je voulais que le booléen par défaut soit vrai ... Je devrais probablement écrire un message complet à ce sujet, si je pouvais trouver le temps.


Belle solution, beaucoup de taille!