Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/312.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/137.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用例如Moq和AutoFixture创建模拟对象和匿名对象的混合体?_C#_Mocking_Moq_Autofixture_Anonymous Objects - Fatal编程技术网

C# 使用例如Moq和AutoFixture创建模拟对象和匿名对象的混合体?

C# 使用例如Moq和AutoFixture创建模拟对象和匿名对象的混合体?,c#,mocking,moq,autofixture,anonymous-objects,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; } } var fixture = new Fixtu

我在工作中遇到了一个类似这样的类:

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;
  }  
}
var fixture = new Fixture();
var anyMyObject = fixture.CreateAnonymous<MyObject>();
问题是,我有一些代码可以访问A、B和C,并调用GetSomeValue()方法(现在,我想说这不是一个好的设计,但有时我会束手无策;-)。我想创建这个对象的模拟,同时将a、B和C设置为一些值。因此,当我使用最小起订量时:

var m = new Mock<MyObject>() { DefaultValue = DefaultValue.Mock };
var m=new Mock(){DefaultValue=DefaultValue.Mock};
让我在GetSomeValue()方法上设置一个结果,但所有属性都设置为null(使用setup()设置所有属性非常麻烦,因为实际对象是一个讨厌的数据对象,并且比上面简化的示例中的属性更多)

另一方面,使用AutoFixture如下所示:

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;
  }  
}
var fixture = new Fixture();
var anyMyObject = fixture.CreateAnonymous<MyObject>();
var fixture=newfixture();
var anyMyObject=fixture.CreateAnonymous();
使我无法调用GetSomeValue()方法

有没有办法将两者结合起来,拥有匿名值和设置调用结果的能力

编辑

根据nemesv的回答,我推导出了以下实用方法(希望我得到了正确的答案):

publicstaticmock-AnonymousMock(),其中T:class
{
var mock=new mock();
fixture.Customize(c=>c.FromFactory(()=>mock.Object));
fixture.CreateAnonymous();
夹具。定制。移除(0);
返回模拟;
}

可能有更好的原因,但这是可行的:

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);
//...
var fixture=newfixture();
var moq=new Mock(){DefaultValue=DefaultValue.Mock};
moq.Setup(m=>m.GetSomeValue()).Returns(3);
fixture.Customize(c=>c.FromFactory(()=>moq.Object));
var anyMyObject=fixture.CreateAnonymous();
AreEqual(3,anyMyObject.GetSomeValue());
Assert.IsNotNull(anyMyObject.A);
//...
最初我尝试使用
fixture.Register(()=>moq.Object)
而不是
fixture。自定义
,但它使用
省略autoproperties()
注册创建者函数,因此它不适用于您的情况。

这实际上可以使用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));
    }
}
MockPostprocessor
MockConstructorQuery
MockRelay
类在to AutoFixture中定义,因此您需要添加对此库的引用。但是,请注意,不需要添加
自动moqcustomization

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;
        }
    }
}
公共类AutoExceptMoqPropertiesCommand:AutoPropertiesCommand
{
公共自动例外moqPropertiesCommand()
:base(新的NoInterceptorsSpecification())
{
}
受保护的覆盖类型GetSpecimentType(对象样本)
{
返回sample.GetType();
}
私有类NoInterceptor规范:IRequestSpecification
{
公共布尔满足(对象请求)
{
var fi=请求作为FieldInfo;
如果(fi!=null)
{
如果(fi.Name==“\uu拦截器”)
返回false;
}
返回true;
}
}
}

此解决方案提供了该问题的一般解决方案。但是,它还没有经过广泛的测试,所以我很想得到反馈。

从3.20.0开始,您可以使用
自动配置MoqCustomization
。这将自动配置所有模拟,以便其成员的返回值由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);
var fixture=newfixture().Customize(新的自动配置moqcustomization());
var mock=fixture.Create();
Assert.NotNull(mock.Object.A);
Assert.NotNull(mock.Object.B);
Assert.NotNull(mock.Object.C);

这是一个很好的解决方案,特别是因为它涵盖了嵌套类型,但是,为了更一般地使用嵌套类型,有没有办法去掉部分:t==typeof(MyObject)| t==typeof(MyParent)?我想到的逻辑是这样的:“如果混合的嵌套类型是可模仿的,那么将其设置为混合类型,否则将其设置为匿名值”。是的,只需将谓词替换为更一般的值。谢谢!我尝试使用(t!=null&&!t.IsPrimitive&&(t.GetConstructors(BindingFlags.Public).Length!=0 | | t.GetConstructor(Type.EmptyTypes)!=null)的谓词,它似乎有效!你能看到这里少了什么吗?我将尝试对它进行一些测试,并尝试让您知道是否有任何结果。好吧,这可能已经足够好了-它是否正确最终取决于您希望以这种方式创建的类型。