2015-12-03 11 views
5

Mit AutoFixture mit dem AutoFixture.AutoMoq Paket, ich Tests manchmal finden, die nicht richtig konfiguriert wurde, um die Sache zu testen sie zu testen gemeint, aber das Problem wurde nie wegen der Standard (losen) Mock Verhalten entdeckt:Wie kann ich AutoMoqCustomization Strict MockBehavior verwenden?

public interface IService 
{ 
    bool IsSomethingTrue(int id); 
} 

void Main() 
{ 
    var fixture = new Fixture() 
     .Customize(new AutoMoqCustomization()); 
    var service = fixture.Freeze<Mock<IService>>(); 
    Console.WriteLine(service.Object.IsSomethingTrue(1)); // false 
} 

Ich möchte, dass Mocks mit Strict-Verhalten erstellt werden, also müssen wir Setup() für die Methoden anrufen, von denen wir erwarten, dass sie aufgerufen werden. Ich kann, wie dies für jeden einzelnen Mock dies tun:

fixture.Customize<Mock<IService>>(c => c.FromFactory(() => new Mock<IService>(MockBehavior.Strict))); 

aber nach AutoMoqCustomization durch Quellcode Kämme() und die verschiedenen ISpecimenBuilder und andere Implementierungen, bin ich ziemlich in Bezug auf die beste Art und Weise verliert nur alle machen Mocks werden mit striktem Verhalten initialisiert. Das Framework scheint sehr flexibel und erweiterbar zu sein, also bin ich sicher, dass es einen einfachen Weg dafür gibt - ich kann einfach nicht herausfinden, wie.

Antwort

3

Es gibt keine einfache integrierte Funktion, mit der Sie etwas tun können, aber es sollte nicht , dass schwer zu tun.

Im Grunde müssen Sie MockConstructorQuery ändern, so dass es den Konstruktor aufruft, die einen MockBehavior Wert annimmt, und übergeben Sie in MockBehavior.Strict.

Jetzt

, können Sie nicht Änderung das Verhalten in MockConstructorQuery, aber die Klasse ist nur einig 9-10 Zeilen Code, so sollten Sie eine neue Klasse erstellen können, die IMethodQuery implementiert durch MockConstructorQuery als Ausgang mit Punkt.

Ebenso werden Sie auch ein benutzerdefinierte erstellen müssen ICustomization, die fast genau die gleiche Funktion wie AutoMoqCustomization, mit der einzigen Ausnahme, dass es Ihre individuellen IMethodQuery mit strengen Mock-Konfiguration verwendet statt MockConstructorQuery. Das sind weitere 7 Codezeilen, die Sie schreiben müssen.

All das sagte, nach meiner Erfahrung mit strengen Mocks ist eine schlechte Idee. Es wird Ihre Tests spröde machen und Sie werden viel Zeit damit verschwenden, "kaputte" Tests zu reparieren. Ich kann nur empfehlen, dass du das nicht tust, aber jetzt habe ich dich gewarnt; es ist dein Fuß.

+0

Danke für das Feedback. Ich werde sehen, ob ich es zum Laufen bringen kann. In Bezug auf strenge Mocks: Wenn die Mocks wirklich "Mocks" waren, denke ich, dass Ihr Punkt gültig ist. Unglücklicherweise dienen die meisten meiner Mocks tatsächlich dem Zweck von * Stubs *, von denen erwartet wird, dass sie einen Wert zurückgeben. Wenn das SUT auf Daten zurückgreift, die von dem Stub zurückgegeben werden, und ich den Stub nicht eingerichtet habe, wird der Test im besten Fall fehlschlagen, und im schlimmsten Fall wird der Test aus dem falschen Grund bestanden. In beiden Fällen würde ich lieber sofort die Codezeile sehen, die anzeigt, welcher Stub konfiguriert werden muss, anstatt daraus zu schließen, woher der NRE kommt. – StriplingWarrior

+0

@StriplingWarrior http://blog.ploeh.dk/2013/10/23/mocks-for-commands-stubs-for-queries –

+0

Ich finde das Beispiel dieses Artikels, um ziemlich künstlich zu sein: Brechen CQS, um ein Benutzerobjekt zu schaffen und zurückzubringen Das hat nicht die ID, die der GetUser-Methode bereitgestellt wird.Das sind nicht die Arten von Änderungen, die ich normalerweise bei Methoden sehe. Die Änderungen, die ich regelmäßig sehe, würden sowieso eine Änderung des Komponententests erforderlich machen, obwohl sie wie das letzte korrekte Beispiel in diesem Artikel geschrieben sind. Wenn dies passiert, finde ich, dass das schnelle Verhalten von Strict Mocks viel Zeit spart und - was noch wichtiger ist - hilft, sicherzustellen, dass meine Komponententests testen, was sie angeblich testen. – StriplingWarrior

0

Für diejenigen, die interessiert sind, unten finden Sie @ MarkSeannanns Antwort in Code übersetzt. Ich bin mir ziemlich sicher, dass es nicht alle Anwendungsfälle abdeckt und es wurde nicht stark getestet. Aber es sollte ein guter Ausgangspunkt sein.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Reflection; 
using Moq; 
using Ploeh.AutoFixture; 
using Ploeh.AutoFixture.AutoMoq; 
using Ploeh.AutoFixture.Kernel; 

namespace ConsoleApplication1 
{ 
    public class StrictAutoMoqCustomization : ICustomization 
    { 
     public StrictAutoMoqCustomization() : this(new MockRelay()) { } 

     public StrictAutoMoqCustomization(ISpecimenBuilder relay) 
     { 
      // TODO Null check params 
      Relay = relay; 
     } 

     public ISpecimenBuilder Relay { get; } 

     public void Customize(IFixture fixture) 
     { 
      // TODO Null check params 
      fixture.Customizations.Add(new MockPostprocessor(new MethodInvoker(new StrictMockConstructorQuery()))); 
      fixture.ResidueCollectors.Add(Relay); 
     } 
    } 

    public class StrictMockConstructorMethod : IMethod 
    { 
     private readonly ConstructorInfo ctor; 
     private readonly ParameterInfo[] paramInfos; 

     public StrictMockConstructorMethod(ConstructorInfo ctor, ParameterInfo[] paramInfos) 
     { 
      // TODO Null check params 
      this.ctor = ctor; 
      this.paramInfos = paramInfos; 
     } 

     public IEnumerable<ParameterInfo> Parameters => paramInfos; 

     public object Invoke(IEnumerable<object> parameters) => ctor.Invoke(parameters?.ToArray() ?? new object[] { }); 
    } 

    public class StrictMockConstructorQuery : IMethodQuery 
    { 
     public IEnumerable<IMethod> SelectMethods(Type type) 
     { 
      if (!IsMock(type)) 
      { 
       return Enumerable.Empty<IMethod>(); 
      } 

      if (!GetMockedType(type).IsInterface && !IsDelegate(type)) 
      { 
       return Enumerable.Empty<IMethod>(); 
      } 

      var ctor = type.GetConstructor(new[] { typeof(MockBehavior) }); 

      return new IMethod[] 
      { 
       new StrictMockConstructorMethod(ctor, ctor.GetParameters()) 
      }; 
     } 

     private static bool IsMock(Type type) 
     { 
      return type != null && type.IsGenericType && typeof(Mock<>).IsAssignableFrom(type.GetGenericTypeDefinition()) && !GetMockedType(type).IsGenericParameter; 
     } 

     private static Type GetMockedType(Type type) 
     { 
      return type.GetGenericArguments().Single(); 
     } 

     internal static bool IsDelegate(Type type) 
     { 
      return typeof(MulticastDelegate).IsAssignableFrom(type.BaseType); 
     } 
    } 
} 

Nutzungs

var fixture = new Fixture().Customize(new StrictAutoMoqCustomization());