1

J'essaie d'apprendre la syntaxe de Rhino Mocks AAA, et j'ai du mal à affirmer qu'une certaine méthode (avec n'importe quelle valeur d'argument) a été appelée. J'utilise Machine.Specifications comme cadre de test.Comment AssertWasCalled une méthode générique avec trois types différents en utilisant RhinoMocks?

Cette méthode particulière est générique et je veux m'assurer qu'elle a été appelée trois fois avec trois types différents.

repo.Save<T1>(anything), repo.Save<T2>(anything), and repo.Save<T3>(anything) 

J'ai écrasé la fonction pour chaque type. Mais je reçois un résultat intéressant. (ci-dessous)

[Subject("Test")] 
public class When_something_happens_with_constraint 
{ 
    static IRepository repo; 
    static TestController controller; 
    static ActionResult result; 

    Establish context =() => 
    { 
     repo = MockRepository.GenerateMock<IRepository>(); 
     controller = new TestController(repo); 
     repo.Stub(o => o.Save<Something>(Arg<Something>.Is.Anything)); 
     repo.Stub(o => o.Save<SomethingElse>(Arg<SomethingElse>.Is.Anything)); 
     repo.Stub(o => o.Save<AnotherOne>(Arg<AnotherOne>.Is.Anything)); 
    }; 

    //post data to a controller 
    Because of =() => { result = controller.SaveAction(new SomethingModel() { Name = "test", Description = "test" }); }; 

    //controller constructs its own something using the data posted, then saves it. I want to make sure three calls were made. 
    It Should_save_something =() => repo.AssertWasCalled(o => o.Save<Somethign>(Arg<Something>.Is.Anything)); 
    It Should_save_something_else =() => repo.AssertWasCalled(o => o.Save<SomethingElse>(Arg<SomethingElse>.Is.Anything)); 
    It Should_save_another_one =() => repo.AssertWasCalled(o => o.Save<AnotherOne>(Arg<AnotherOne>.Is.Anything)); 
} 

Le résultat est deux exceptions et une passe.

Le premier appel lance:

System.InvalidOperationException: Aucune attentes ont été installés à vérifier, assurez-vous que l'appel de méthode dans l'action est un virtuel (C#)/Overridable (VB.Net) appel de méthode

La seconde lance:

System.InvalidOperationException: Utiliser Arg seulement dans un appel de méthode maquette en cours d'enregistrement. 1 arguments attendus, 2 ont été définis.

Le troisième passe ... pour une raison étrange.

J'ai également essayé d'utiliser GenerateMock() avec Expect dans ma configuration et d'utiliser GenerateStub() avec Stub. Les deux ont fini avec le même résultat. Je dois faire quelque chose de mal.

J'utilise: MachineSpec 0.3.0.0 et 3.6.0.0 RhinoMocks

Toutes les idées?

----- ---------- FIXE

Voici la complète (version de travail) avec l'aide de Lee. J'utilise une couche supplémentaire (non-linq). Mon problème réel était que l'un de mes tests réutilisait la mauvaise variable lambda dans le code réel hors ligne. Il Should_do_something =() => repo.AssertWasCalled (o =>repo .Save (données)); // mauvais lambda

Voici donc un exemple de test correct pour référence.

using System; 
using System.Linq; 
using System.Collections.Generic; 
using Machine.Specifications; 
using Rhino.Mocks; 

namespace OnlineTesting.Specifications 
{ 
    public interface Repository 
    { 
     void Save<T>(T data); 
     IQueryable<T> All<T>(); 
    } 

    public interface Service 
    { 
     void SaveItem(Item data); 
     void SaveAnotherItem(AnotherItem data); 
     void SaveOtherItem(OtherItem data); 
     List<Item> GetItems(); 
     List<AnotherItem> GetAnotherItems(); 
     List<OtherItem> GetOtherItems(); 
    } 

    public class ConcreteService : Service 
    { 
     Repository repo; 
     public ConcreteService(Repository repo) 
     { 
      this.repo = repo; 
     } 
     public void SaveItem(Item data) 
     { 
      repo.Save(data); 
     } 
     public void SaveAnotherItem(AnotherItem data) 
     { 
      repo.Save(data); 
     } 
     public void SaveOtherItem(OtherItem data) 
     { 
      repo.Save(data); 
     } 

     public List<Item> GetItems() 
     { 
      return repo.All<Item>().ToList(); 
     } 
     public List<AnotherItem> GetAnotherItems() 
     { 
      return repo.All<AnotherItem>().ToList(); 
     } 
     public List<OtherItem> GetOtherItems() 
     { 
      return repo.All<OtherItem>().ToList(); 
     } 
    } 

    public class Item 
    { 
     public int Id { get; set; } 
    } 
    public class OtherItem 
    { 
    } 
    public class AnotherItem 
    { 
    } 


    public class When_something_else_happens 
    { 
     Establish context =() => 
     { 
      _repository = MockRepository.GenerateMock<Repository>(); 
      _service = new ConcreteService(_repository); 
      _controller = new TestController(_service); 

      _repository.Stub(o => o.Save<Item>(Arg<Item>.Is.Anything)).WhenCalled(
       new Action<MethodInvocation>((o) => 
       { 
        var data = o.Arguments.FirstOrDefault() as Item; 
        if (data != null && data.Id == 0) 
         data.Id++; 
       })); 
     }; 

     Because of =() => _controller.DoSomethingElse(); 

     It should_save_the_first_thing =() => 
      _repository.AssertWasCalled(repo => repo.Save(Arg<Item>.Is.Anything)); 

     It should_save_the_other_thing =() => 
      _repository.AssertWasCalled(repo => repo.Save(Arg<OtherItem>.Is.Anything)); 

     It should_save_the_last_thing =() => 
      _repository.AssertWasCalled(repo => repo.Save(Arg<AnotherItem>.Is.Anything)); 

     static Repository _repository; 
     static TestController _controller; 
     static Service _service; 
    } 

    public class TestController 
    { 
     readonly Service _service; 

     public TestController(Service service) 
     { 
      _service = service; 
     } 

     public void DoSomethingElse() 
     { 
      _service.SaveItem(new Item()); 
      _service.SaveOtherItem(new OtherItem()); 
      _service.SaveAnotherItem(new AnotherItem()); 
     } 
    } 
} 
+0

Aussi ... Je ne peux pas configurer les arguments avant l'appel car l'action du contrôleur construit un nouvel objet et le passe au référentiel. Donc j'utilise Arg .Il n'y a rien. Je veux juste m'assurer que repo.save a été appelé pour un type spécifique. – Nathan

Répondre

0

Faites un essai.

[Subject("Test")] 
public class When_something_happens_with_constraint 
{ 
    static IRepository repo; 
    static TestController controller; 
    static ActionResult result; 

    Establish context =() => 
    { 
     repo = MockRepository.GenerateMock<IRepository>(); 
     controller = new TestController(repo); 
    }; 

    //post data to a controller 
    Because of =() => result = controller.SaveAction(new SomethingModel() { Name = "test", Description = "test" }); 

    //controller constructs its own something using the data posted, then saves it. I want to make sure three calls were made. 
    It Should_save_something =() => repo.AssertWasCalled(o => o.Save(Arg<Something>.Is.Anything)); 
    It Should_save_something_else =() => repo.AssertWasCalled(o => o.Save(Arg<SomethingElse>.Is.Anything)); 
    It Should_save_another_one =() => repo.AssertWasCalled(o => o.Save(Arg<AnotherOne>.Is.Anything)); 
} 
+0

Je pourrais même aller jusqu'à créer un SomethingModel au niveau du module pour que vous puissiez vérifier qu'il s'agit bel et bien de celui qui est envoyé au dépôt pour sauvegarde. – leebrandt

+0

Merci Lee. Après avoir testé le vôtre et l'avoir comparé au mien, j'ai trouvé le problème. J'ai posté le résultat complet ci-dessus. – Nathan

1

Le point que tout le monde semble être plus lustrage est que vous ne pas besoin de bouchonner afin d'effectuer une « assert a été appelé ». Et, la façon dont vous l'avez écrit, Arg<T>.Is.Anything, il va ignorer le type. Vous ne vérifiez aucune contrainte générique. Vous voulez utiliser Arg<T>.Is.TypeOf<T>. Vérifiez le documentation pour plus de détails.

----------------------------------------------- 
| Arg<T>.Is |        | 
=============================================== 
| Anything() | No constraints    | 
----------------------------------------------- 
| TypeOf<T>() | Argument is of a certain type | 
----------------------------------------------- 

Votre extrait fixe de code est encore trop compliqué. Vous n'utilisez pas l'objet data que vous enregistrez dans le "lorsque vous êtes appelé". Et vous n'en avez pas besoin pour faire un simple "affirmation a été appelé".

Le code de leebrandt semble aussi correct et simple que possible, sans introduire de conteneur d'auto-stockage.