Создание гибридного макета и анонимного объекта с использованием, например, Moq и AutoFixture? - PullRequest
6 голосов
/ 18 февраля 2012

Я столкнулся с классом во время моей работы, который выглядит так:

public class MyObject
{
  public int? A {get; set;}
  public int? B {get; set;}
  public int? C {get; set;}
  public virtual int? GetSomeValue()
  {
    //simplified behavior:
    return A ?? B ?? C;
  }  
}

Проблема в том, что у меня есть некоторый код, который обращается к A, B и C и вызывает метод GetSomeValue () (сейчас я бы сказал, что это не очень хороший дизайн, но иногда мои руки связаны ;-)). Я хочу создать макет этого объекта, в котором в то же время для A, B и C установлены некоторые значения. Итак, когда я использую moq как таковой:

var m = new Mock<MyObject>() { DefaultValue = DefaultValue.Mock };

позволяет мне настроить результат с помощью метода GetSomeValue (), но все свойства имеют значение NULL (и установка всех из них с помощью Setup () довольно громоздка, поскольку реальный объект является неприятным объектом данных и имеет больше свойств чем в приведенном выше упрощенном примере).

Так, с другой стороны, используя AutoFixture, как это:

var fixture = new Fixture();
var anyMyObject = fixture.CreateAnonymous<MyObject>();

Оставляет меня без возможности остановить вызов метода GetSomeValue ().

Есть ли способ объединить эти два, чтобы иметь анонимные значения и возможность настроить результаты вызовов?

Редактировать

Основываясь на ответе nemesv, я получил следующий полезный метод (надеюсь, я правильно понял):

public static Mock<T> AnonymousMock<T>() where T : class
{
  var mock = new Mock<T>();
  fixture.Customize<T>(c => c.FromFactory(() => mock.Object));
  fixture.CreateAnonymous<T>();
  fixture.Customizations.RemoveAt(0);
  return mock;
}

Ответы [ 3 ]

5 голосов
/ 19 февраля 2012

Это на самом деле возможно сделать с автофиксом, но это требует небольшой настройки.Точки расширяемости все есть, но я допускаю, что в этом случае решение не особенно доступно для обнаружения.

Это становится еще сложнее, если вы хотите, чтобы оно работало с вложенными / сложными типами.

Учитывая приведенный выше класс MyObject, а также класс MyParent:

public class MyParent
{
    public MyObject Object { get; set; }

    public string Text { get; set; }
}

, все эти модульные тесты проходят:

public class Scenario
{
    [Fact]
    public void CreateMyObject()
    {
        var fixture = new Fixture().Customize(new MockHybridCustomization());

        var actual = fixture.CreateAnonymous<MyObject>();

        Assert.NotNull(actual.A);
        Assert.NotNull(actual.B);
        Assert.NotNull(actual.C);
    }

    [Fact]
    public void MyObjectIsMock()
    {
        var fixture = new Fixture().Customize(new MockHybridCustomization());

        var actual = fixture.CreateAnonymous<MyObject>();

        Assert.NotNull(Mock.Get(actual));
    }

    [Fact]
    public void CreateMyParent()
    {
        var fixture = new Fixture().Customize(new MockHybridCustomization());

        var actual = fixture.CreateAnonymous<MyParent>();

        Assert.NotNull(actual.Object);
        Assert.NotNull(actual.Text);
        Assert.NotNull(Mock.Get(actual.Object));
    }

    [Fact]
    public void MyParentIsMock()
    {
        var fixture = new Fixture().Customize(new MockHybridCustomization());

        var actual = fixture.CreateAnonymous<MyParent>();

        Assert.NotNull(Mock.Get(actual));
    }
}

Что входит в MockHybridCustomization?Это:

public class MockHybridCustomization : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Customizations.Add(
            new MockPostprocessor(
                new MethodInvoker(
                    new MockConstructorQuery())));
        fixture.Customizations.Add(
            new Postprocessor(
                new MockRelay(t =>
                    t == typeof(MyObject) || t == typeof(MyParent)),
                new AutoExceptMoqPropertiesCommand().Execute,
                new AnyTypeSpecification()));
    }
}

Классы MockPostprocessor, MockConstructorQuery и MockRelay определены в расширении AutoMoq для AutoFixture, поэтому вам нужно добавить ссылку на этобиблиотека.Тем не менее, обратите внимание, что не требуется добавлять AutoMoqCustomization.

. Класс AutoExceptMoqPropertiesCommand также настраивается по этому случаю:

public class AutoExceptMoqPropertiesCommand : AutoPropertiesCommand<object>
{
    public AutoExceptMoqPropertiesCommand()
        : base(new NoInterceptorsSpecification())
    {
    }

    protected override Type GetSpecimenType(object specimen)
    {
        return specimen.GetType();
    }

    private class NoInterceptorsSpecification : IRequestSpecification
    {
        public bool IsSatisfiedBy(object request)
        {
            var fi = request as FieldInfo;
            if (fi != null)
            {
                if (fi.Name == "__interceptors")
                    return false;
            }

            return true;
        }
    }
}

Это решение предоставляет общее решение длявопрос.Тем не менее, он не был тщательно протестирован, поэтому я хотел бы получить отзыв об этом.

4 голосов
/ 18 февраля 2012

Вероятно, есть лучшее почему, но это работает:

var fixture = new Fixture();
var moq = new Mock<MyObject>() { DefaultValue = DefaultValue.Mock };
moq.Setup(m => m.GetSomeValue()).Returns(3);

fixture.Customize<MyObject>(c => c.FromFactory(() => moq.Object));

var anyMyObject = fixture.CreateAnonymous<MyObject>();

Assert.AreEqual(3, anyMyObject.GetSomeValue());
Assert.IsNotNull(anyMyObject.A);
//...

Изначально я пытался использовать fixture.Register(() => moq.Object); вместо fixture.Customize, но он регистрирует функцию создателя с OmitAutoProperties(), поэтому она не будет работать для вас.

2 голосов
/ 21 августа 2014

Начиная с 3.20.0, вы можете использовать AutoConfiguredMoqCustomization. Это автоматически сконфигурирует все макеты так, что возвращаемые значения их членов будут сгенерированы AutoFixture.

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

var mock = fixture.Create<Mock<MyObject>>();

Assert.NotNull(mock.Object.A);
Assert.NotNull(mock.Object.B);
Assert.NotNull(mock.Object.C);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...