2015-12-03 4 views
5

Использование AutoFixture с пакетом AutoFixture.AutoMoq, я иногда найти тесты, которые не были настроены правильно проверить вещи они имели в виду, чтобы проверить, но проблема так и не был обнаружен из-за Mock поведение по умолчанию (Свободный):Как сделать AutoMoqCustomization использовать Strict MockBehavior?

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 
} 

Я хочу, чтобы Mocks создавался с использованием Строгого поведения, поэтому мы вынуждены вызывать Setup() для методов, которые мы ожидаем назвать. Я могу сделать это для каждого отдельного издеваться, как это:

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

Но после расчесывания через исходный код для AutoMoqCustomization() и различных ISpecimenBuilder и других реализаций, я довольно потерял, чтобы наилучшим образом, чтобы просто сделать все Mocks инициализируется строгим поведением. Структура представляется очень гибкой и расширяемой, поэтому я уверен, что есть простой способ сделать это - я просто не могу понять, как это сделать.

ответ

3

Нет простой встроенной функции, которая позволит вам сделать что-то подобное, но не должно быть , что трудно сделать.

По существу, вам нужно будет изменить MockConstructorQuery так, чтобы он вызывал конструктор, который принимает значение MockBehavior, и передает MockBehavior.Strict.

Теперь, вы не можете Смените, что поведение в MockConstructorQuery, но этот класс только некоторые 9-10 строк кода, так что вы должны быть в состоянии создать новый класс, который реализует IMethodQuery с помощью MockConstructorQuery в качестве исходного точка.

Кроме того, вам также необходимо создать пользовательский ICustomization, что делает почти точно так же, как AutoMoqCustomization, с той лишь разницей, что он использует свой обычай IMethodQuery со строгой макете конфигурации вместо MockConstructorQuery. Это еще 7 строк кода, которые вам нужно написать.

Все, что сказал, по моему опыту, с использованием строгих макетов - плохая идея. Это сделает ваши тесты хрупкими, и вы потратите много времени на исправление «сломанных» тестов. Я могу только рекомендовать, чтобы вы этого не делали, но теперь я предупреждал вас; это твоя нога.

+0

Спасибо за обратную связь. Я посмотрю, смогу ли я заставить его работать. Что касается строгих издевательств: если издевки были действительно «насмешливыми», я думаю, что ваша точка действительна. К сожалению, большинство моих издевательств действительно служат цели * stubs *, которые, как ожидается, вернут значение. Если SUT полагается на данные, возвращенные из заглушки, и я не настроил заглушку, тогда в лучшем случае тест завершится неудачно, и в худшем случае тест пройдет по неправильной причине. Я бы предпочел в любом случае мгновенно увидеть строку кода, показывающую, какая заглушка нуждается в настройке, вместо того, чтобы выводить, откуда приходит NRE. – StriplingWarrior

+0

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

+0

Я нахожу, что пример статьи довольно надуманный: нарушение CQS для создания и возврата пользовательского объекта который даже не имеет идентификатора, предоставленного методу GetUser.Это не те изменения, которые я обычно вижу в методах. Изменения, которые я вижу регулярно, в любом случае потребуют изменения в модульном тесте, даже если они написаны как окончательный правильный пример в этой статье. Когда это происходит, я обнаружил, что быстрое и быстрое выполнение Strict mocks экономит много времени и, что еще важнее, помогает гарантировать, что мои модульные тесты проверяют то, что они утверждают, что тестируют. – StriplingWarrior

0

Для интересующихся ниже, вы можете найти ответ @ MarkSeemann, переведенный в код. Я уверен, что он не охватывает все варианты использования, и он не был сильно протестирован. Но это должно быть хорошей отправной точкой.

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); 
     } 
    } 
} 

Использование

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