Создание гибрида макета и анонимного объекта с использованием EG MOQ и AutoFixture?

StackOverflow https://stackoverflow.com/questions/9341413

Вопрос

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

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 () (теперь, я бы сказал, что это не хороший дизайн, но иногда мои руки связаны ;-)). Я хочу создать макет этого объекта, который, в то же время, имеет установленное значение, 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;
}
Это было полезно?

Решение

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

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

Учитывая 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));
    }
}

Что в Mockybridcustomization? Этот:

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 классы определены в Расширение автомобиля Для автофиктуры, поэтому вам нужно добавить ссылку на эту библиотеку. Однако обратите внимание, что не обязательно добавлять 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;
        }
    }
}

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

Другие советы

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

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() Так что это не сработает для вас.

По состоянию на 3.20.0 вы можете использовать AutoConfiguredMoqCustomization. Анкет Это автоматически настраивает все макет, так что возвращаемые значения их членов генерируются автофикцией.

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);
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top