1

我正在尝试编写一些测试,我使用 xUnit.net、Moq、AutoFixture。我需要为我的测试方法注入服务:

[Theory, AutoData]
public void TestSmthCool(IService service)
{
}

IService我想模拟 3 个依赖项。但是,如果我运行测试,我会收到错误:

AutoFixture was unable to create an instance from Services.Interfaces.IService because it's an interface.

因此,我通过以下方式对其进行了修复:

[Theory, AutoData]
public void TestSmthCool()
{
   var fixture = new Fixture();
   fixture.Customize(new AutoMoqCustomization());
   fixture.Customizations.Add(
      new TypeRelay(
         typeof(IService),
         typeof(MyService)
      )
   );

   var s= fixture.Create<IService>();
}

但是,如何设置TypeRelay所有测试并通过方法构造函数注入服务?

4

2 回答 2

2

如果你想使用MyServicefor IService,那么你不需要AutoMoqCustomization; 即这个测试通过:

[Fact]
public void TestSmthCool()
{
    var fixture = new Fixture();
    fixture.Customizations.Add(
        new TypeRelay(
            typeof(IService),
            typeof(MyService)
        )
    );

    var s = fixture.Create<IService>();

    Assert.IsAssignableFrom<MyService>(s);
}

如果要自动执行此操作,可以首先将其打包TypeRelayICustomization

public class MyServiceCustomization : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Customizations.Add(
           new TypeRelay(
              typeof(IService),
              typeof(MyService)));
    }
}

然后创建一个派生自 的属性AutoDataAttribute

public class MyServiceAutoDataAttribute : AutoDataAttribute
{
    public MyServiceAutoDataAttribute() :
        base(new Fixture().Customize(new MyServiceCustomization()))
    {
    }
}

然后,您可以在所有测试中使用它:

[Theory, MyServiceAutoData]
public void CustomizedAutoDataBasedTest(IService s)
{
    Assert.IsAssignableFrom<MyService>(s);
}

一般来说,我倾向于创建一个代码库范围CompositeCustomization,我不加选择地应用于所有测试。

于 2017-09-27T06:51:54.630 回答
0

我只是能够通过将接口/具体类实现包装在一个参数属性中来做到这一点,该属性添加了一个自定义,这反过来又创建了一个 TypeRelay

请参见下面的示例(顺便说一下,它在 .net 6 中)

public interface IService
{
    string Echo(string val);
}

public class MyService : IService
{
    public string Echo(string val)
    {
        return val + "Example";
    }
}

public interface IService2
{
    string Echo(string val);
}

public class MyService2 : IService2
{
    public string Echo(string val)
    {
        return val + "Example2";
    }
}

public sealed class InterfaceMapCustomization : ICustomization
{
    private readonly Type _interfaceType;
    private readonly Type _concreteType;
    public InterfaceMapCustomization(Type interfaceType, Type concreteType)
    {
        if (!interfaceType.IsAssignableFrom(concreteType))
        {
            throw new ArgumentException($"Type '{concreteType.Name}' does not implement interface '{interfaceType.Name}'");
        }

        _interfaceType = interfaceType;
        _concreteType = concreteType;
    }

    public void Customize(IFixture fixture)
    {
        fixture.Customizations.Add(new TypeRelay(_interfaceType, _concreteType));
    }
}

/// <summary>
/// Adds a TypeRelay to the Fixture customizations for the specified interface/class pair
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class InterfaceMapAttribute : CustomizeAttribute
{
    private readonly Type _interfaceType;
    private readonly Type _concreteType;

    /// <summary>
    /// Adds a TypeRelay to the Fixture customizations for the specified interface/class pair
    /// </summary>
    /// <param name="interfaceType">The interface to which we want to map our type to</param>
    /// <param name="concreteType">The class implementing the interface we want to create</param>
    /// <exception cref="InvalidOperationException"></exception>
    public InterfaceMapAttribute(Type interfaceType, Type concreteType)
    {
        if (!interfaceType.IsAssignableFrom(concreteType))
        {
            throw new InvalidOperationException($"Type '{concreteType.Name}' does not implement interface '{interfaceType.Name}'");
        }

        _interfaceType = interfaceType;
        _concreteType = concreteType;
    }

    public override ICustomization GetCustomization(ParameterInfo parameter)
    {
        if (parameter == null)
        {
            throw new InvalidOperationException("Parameter info is null");
        }

        if (parameter.ParameterType != _interfaceType)
        {
            throw new InvalidOperationException($"Parameter '{parameter.Name}' does not implement interface '{_interfaceType.Name}'");
        }

        return new CompositeCustomization(new List<ICustomization>
        {
            new InterfaceMapCustomization(_interfaceType, _concreteType)
        });
    }
}



public class UnitTest1
{

    [Theory]
    [AutoData]
    public void TestSomething(
        string expected,
        [InterfaceMap(typeof(IService), typeof(MyService))] IService sut,
        [InterfaceMap(typeof(IService2), typeof(MyService2))] IService2 sut2
        )
    {
        var result = sut.Echo(expected);

        Assert.Equal(expected + "Example", result);

        var result2 = sut2.Echo(expected);

        Assert.Equal(expected + "Example2", result2);
    }
}
于 2022-02-11T03:21:36.350 回答