c# - 使用例如创建模拟对象和匿名对象的混合体最小起订量和 AutoFixture?

标签 c# mocking moq autofixture anonymous-objects

我在工作中遇到过这样一个类:

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 设置为某些值。所以,当我这样使用最小起订量时:

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

最佳答案

实际上可以用 AutoFixture 来做,但它确实需要一些调整。可扩展性点都在那里,但我承认在这种情况下,解决方案并不是特别容易发现。

如果你想让它处理嵌套/复杂类型,那就更难了。

给定上面的 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()));
    }
}

MockPostprocessorMockConstructorQueryMockRelay 类在 AutoMoq extension 中定义到 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;
        }
    }
}

此解决方案提供了问题的通用解决方案。但是,它尚未经过广泛测试,因此我很乐意收到有关它的反馈。

关于c# - 使用例如创建模拟对象和匿名对象的混合体最小起订量和 AutoFixture?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9341413/

相关文章:

c#: 元素 'Id' 不匹配任何字段或属性 MongoDB

c# - 文本输入编辑文本 : ArgumentNullException: missing source event info in MvxWeakEventSubscription

c# - 如何对字符串列表进行排序?

python - Python 的 create_autospec 中的实例参数有什么作用?

c# - 最小起订量方法调用顺序

c# - 如何在 C# 中模拟来自 httpclient 的响应,Azure 函数,如何测试

c# - Json.NET Schema 会忽略 $schema 吗?

C#:如何对依赖于同一类中另一个方法的方法进行单元测试?

ruby-on-rails - 未定义的方法 `receive_message_chain'

c# - 在单元测试中正确使用最小起订量