首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >AutoFixture/AutoMoq + NUnit AutoData冻结对象未由AutoMoq接口方法返回

AutoFixture/AutoMoq + NUnit AutoData冻结对象未由AutoMoq接口方法返回
EN

Stack Overflow用户
提问于 2014-08-08 16:02:30
回答 1查看 2K关注 0票数 3

使用AutoMoqCustomization,我本来希望我的测试能成功,但它失败了。

这是一个考验:

代码语言:javascript
复制
[Test, AutoMoqData]
public void Test1(
    [Frozen] MyObject myObject, 
    [Frozen] Mock<IRepo> stubMock, 
    MyClass sut, 
    int objectId)
{
    myObject.Id = objectId;
    MyObject result = sut.GetById(objectId);
    Assert.That(result.Id, Is.EqualTo(myObject.Id));
}

如果我添加一行代码,我就可以让它正常工作。但我不想这么做,因为它应该被推断出来?

代码语言:javascript
复制
    stubMock.Setup(r => r.GetObject(It.IsAny<int>())).Returns(() => myObject);

MyClass有一个带有IRepo的构造函数。这就像一种魅力,因为如果我在测试中使用上面的一行,我就会通过测试。

通常,我编写测试时不需要自动数据--它要详细得多:

代码语言:javascript
复制
[Test]
public void Test3()
{
    IFixture fixture = new Fixture();
    int objectId = fixture.Create<int>();
    var stubMock = fixture.Freeze<Mock<IRepo>>();

    stubMock.Setup(r => r.GetObject(It.IsAny<int>())).Returns(() => fixture.Create<MyObject>());
    fixture.Freeze<MyObject>(customise => customise.With(d => d.Id, objectId));

    var sut = new MyClass(stubMock.Object);
    MyObject result = sut.GetById(objectId);
    Assert.That(result.Id, Is.EqualTo(objectId));
}

所以我的代码已经清晰得多了,但是我喜欢上面最后的那个樱桃:)有什么想法吗?

如何运行:

添加Nuget包:

代码语言:javascript
复制
<packages>
  <package id="AutoFixture" version="3.19.2" targetFramework="net45" />
  <package id="AutoFixture.AutoMoq" version="3.19.2" targetFramework="net45" />
  <package id="AutoFixture.NUnit2" version="3.19.2" targetFramework="net45" />
  <package id="Moq" version="3.1.416.3" targetFramework="net45" />
  <package id="NUnit" version="2.6.3" targetFramework="net45" />
</packages>

完整法典:

代码语言:javascript
复制
public class AutoMoqDataAttribute : AutoDataAttribute
    {
        public AutoMoqDataAttribute()
            : base(new Fixture().Customize(new AutoMoqCustomization()))
        {
        }
    }

    [TestFixture]
    public class Class1
    {
        public interface IRepo
        {
            MyObject GetObject(int id);
        }

        public class MyObject
        {
            public int Id { get; set; }
        }

        public class MyClass
        {
            private readonly IRepo _test;

            public MyClass(IRepo test) { _test = test; }

            public MyObject GetById(int id) { return _test.GetObject(id); }
        }

        [Test, AutoMoqData]
        public void Test1(
            [Frozen] MyObject myObject, [Frozen] IRepo stubMock, MyClass sut, int objectId)
        {
            myObject.Id = objectId;

            // expecting this commented line to be automatic because of the Frozen attribute on myObject? 
            // Mock.Get(stubMock).Setup(r => r.GetObject(It.IsAny<int>())).Returns(() => myObject);

            MyObject result = sut.GetById(objectId);

            Assert.That(result.Id, Is.EqualTo(myObject.Id));
        }

        [Test, AutoMoqData]
        public void Test2(
            [Frozen] MyObject myObject, [Frozen] Mock<IRepo> stubMock, MyClass sut, int objectId)
        {
            myObject.Id = objectId;

            // expecting this commented line to be automatic because of the Frozen attribute on myObject? 
            // stubMock.Setup(r => r.GetObject(It.IsAny<int>())).Returns(() => myObject);

            MyObject result = sut.GetById(objectId);

            Assert.That(result.Id, Is.EqualTo(myObject.Id));
        }

        [Test]
        public void Test3()
        {
            IFixture fixture = new Fixture();
            int objectId = fixture.Create<int>();
            var stubMock = fixture.Freeze<Mock<IRepo>>();

            stubMock.Setup(r => r.GetObject(It.IsAny<int>())).Returns(() => fixture.Create<MyObject>());
            fixture.Freeze<MyObject>(customise => customise.With(d => d.Id, objectId));

            var sut = new MyClass(stubMock.Object);
            MyObject result = sut.GetById(objectId);
            Assert.That(result.Id, Is.EqualTo(objectId));
        }

    }
EN

回答 1

Stack Overflow用户

发布于 2014-08-11 13:06:11

感谢@MarkSeemann给出的一些提示,我正在找到解决这个‘问题’的方法。

我的思想

我从AutoFixture.AutoMoq nuget包中的AutoFixture.AutoMoq类开始。我创建了一个重载MockRelayExtras,并将其添加到AutoMoqCustomozation中。

这将对接口或抽象类进行反思,并相应地设置模拟。

例如。

代码语言:javascript
复制
Mock.Get(TInterface.Instance)
    .Setup( i => i.Method( It.IsAny<TParamType>() ) )
    .Returns( () => fixture.Create<TMethodResult>() );

目前,这只适用于接口和抽象类(没有构造函数)。

这是个开始。不过,代码需要清理一下。另外,我希望其他人会喜欢、喜欢和使用这个吗?

编码愉快!

全码

这是一种可行的解决方案,并通过某种基础测试证明其工作正常。

app.config

(这修复了重新配置测试运行程序)

代码语言:javascript
复制
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Moq" publicKeyToken="69f491c39445e920" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.2.1408.717" newVersion="4.2.1408.717" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="nunit.core.interfaces" publicKeyToken="96d09a1eb7f44a77" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-2.6.3.13283" newVersion="2.6.2.12296" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

nuget包裹:

代码语言:javascript
复制
<packages>
  <package id="AutoFixture" version="3.19.2" targetFramework="net45" />
  <package id="AutoFixture.AutoMoq" version="3.19.2" targetFramework="net45" />
  <package id="AutoFixture.NUnit2" version="3.19.2" targetFramework="net45" />
  <package id="Moq" version="4.2.1408.0717" targetFramework="net45" />
  <package id="NUnit" version="2.6.3" targetFramework="net45" />
</packages>

属性

代码语言:javascript
复制
public class AutoMoqDataAttribute : AutoDataAttribute
{
    public AutoMoqDataAttribute()
        : base(new Fixture().Customize(new AutoMoqCustomization(new MockRelayExtras())))
    {

    }
}

更新的MockRelay类

代码语言:javascript
复制
public class MockRelayExtras : MockRelay, ISpecimenBuilder
{
    public new object Create(object request, ISpecimenContext context)
    {
        object result = base.Create(request, context);

        var requestType = request as Type;
        if (requestType != null && result != null)
        {
            SetupMockWithInterface(requestType, result, context);
        }

        return result;
    }

    public static void SetupMockWithInterface(Type requestType, object result, ISpecimenContext context)
    {
        typeof (MockRelayExtras).GetMethod("SetupMockWithInterfaceGeneric")
            .MakeGenericMethod(requestType).Invoke(null, new[] {result, context});
    }

    public static void SetupMockWithInterfaceGeneric<TType>(TType result, ISpecimenContext context)
        where TType : class
    {
        //Mock.Get(stubMock).Setup(r => r.GetObject(It.IsAny<int>())).Returns(() => myObject);
        Mock<TType> genericMock = Mock.Get(result);

        foreach (MethodInfo methodInfo in typeof (TType).GetMethods())
        {
            // ISetup<TType,TMethodResult> setup = genericMock.Setup<TType,TMethodResult>( methodInfo )
            object setup =
                typeof (MockRelayExtras).GetMethod("SetupAsGenericFunc")
                    .MakeGenericMethod(typeof (TType), methodInfo.ReturnType)
                    .Invoke(null, new object[] {methodInfo, genericMock});

            // setup.Returns( () => context.Create<TResult>() )
            typeof (MockRelayExtras).GetMethod("SetupReturnsAsGenericFunc")
                .MakeGenericMethod(typeof (TType), methodInfo.ReturnType)
                .Invoke(null, new[] {setup, context});
        }
    }

    public static ISetup<TMock, TResult> SetupAsGenericFunc<TMock, TResult>(MethodInfo method, Mock<TMock> mock)
        where TMock : class
    {
        ParameterExpression input = Expression.Parameter(typeof (TMock));

        ParameterInfo[] parameters = method.GetParameters();

        Expression<Func<TMock, TResult>> setup = null;

        if (parameters.Length > 0)
        {
            IEnumerable<MethodCallExpression> properties =
                parameters.Select(
                    pi => Expression.Call(typeof (It), "IsAny", new[] {pi.ParameterType}, new Expression[0]));
            setup = Expression.Lambda<Func<TMock, TResult>>(
                Expression.Call(input, method, properties), input);
        }
        else
        {
            setup = Expression.Lambda<Func<TMock, TResult>>(
                Expression.Call(input, method), input);
        }


        return mock.Setup(setup);
    }

    public static void SetupReturnsAsGenericFunc<TMock, TResult>(ISetup<TMock, TResult> setup,
        ISpecimenContext context)
        where TMock : class
    {
        setup.Returns(() => context.Create<TResult>());
    }
}

测试

代码语言:javascript
复制
[TestFixture]
public class MockRelayExtrasTests
{
    public class RelayTestClass
    {
        public int Id { get; set; }
    }

    public class RandomInputData
        {
            public int Prop1 { get; set; }
            public string Prop2 { get; set; }
        }

    public interface ISingleResultNoInput
    {
        RelayTestClass SingleResultNoInput();
        IEnumerable<RelayTestClass> ListResultNoInput();
        RelayTestClass GetItemById(int id);
        RelayTestClass GetItemById(int id, string randomSearch);
        RelayTestClass GetItemBySearch(RandomInputData data);
    }

    [Test, AutoMoqData]
    public void Create_AutoMockInterfaceIEnumerableResults_InterfaceIsSetupWithIEnumerable(
        [Frozen] RelayTestClass itemTemplate,
        [Frozen] ISingleResultNoInput sut,
        [Frozen] IFixture fixture
        )
    {
        List<RelayTestClass> result = sut.ListResultNoInput().ToList();
        Assert.That(result[0], Is.EqualTo(itemTemplate));
    }

    [Test, AutoMoqData]
    public void Create_AutoMockInterfaceMethodsAutoMoqData_InterfaceIsSetup(
        [Frozen] RelayTestClass frozenItem, [Frozen] ISingleResultNoInput sut, int expectedId)
    {
        frozenItem.Id = expectedId;
        RelayTestClass result = sut.SingleResultNoInput();
        Assert.That(result.Id, Is.EqualTo(expectedId));
    }

    [Test, AutoMoqData]
    public void Create_AutoMockInterfaceMethodParameter_InterfaceIsSetup(
        [Frozen] RelayTestClass frozenItem, [Frozen] ISingleResultNoInput sut, int id)
    {
        frozenItem.Id = id;
        RelayTestClass result = sut.GetItemById(id);
        Assert.That(result.Id, Is.EqualTo(id));
    }

    [Test, AutoMoqData]
    public void Create_AutoMockInterfaceMethodsAutoMoqDataMultipleInput_InterfaceIsSetup(
        [Frozen] RelayTestClass frozenItem, [Frozen] ISingleResultNoInput sut, int id, string searchString)
    {
        frozenItem.Id = id;
        RelayTestClass result = sut.GetItemById(id, searchString);
        Assert.That(result.Id, Is.EqualTo(id));
    }

    [Test, AutoMoqData]
    public void Create_AutoMockInterfaceMethodsAutoMoqDataComplexInput_InterfaceIsSetup(
        [Frozen] RelayTestClass frozenItem, [Frozen] ISingleResultNoInput sut, RandomInputData input)
    {
        frozenItem.Id = input.Prop1;
        RelayTestClass result = sut.GetItemBySearch(input);
        Assert.That(result.Id, Is.EqualTo(input.Prop1));
    }
}

按包自动生成外接程序

代码语言:javascript
复制
[NUnitAddin]
public class LocalAddin : Addin
{
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25207585

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档