【问题标题】:How to cast Moq object to check type for dependency injection如何转换 Moq 对象以检查依赖注入的类型
【发布时间】:2022-09-24 17:49:19
【问题描述】:

我为 Microsoft DependencyInjection 创建了一个包装类,以简化我的应用程序中的一些调用。我正在尝试编写使用 Moq 将对象插入依赖注入服务的单元测试。

using Microsoft.Extensions.DependencyInjection;
using Moq;
using Xunit;

public static class DiService
{
    private static IServiceProvider diProvider;
    private static Dictionary<Type, object> mockCollection = new Dictionary<Type, object>();

    public static IServiceCollection ServiceCollection { get; } = new ServiceCollection();
    public static bool UseTestMode { get; set; } = false;

    public static T GetRequiredService<T>()
        where T : class
    {
        if( UseTestMode )
        {
            if( mockCollection.ContainsKey(typeof(T)) )
            {
                return mockCollection[typeof(T)] as T;
            }
        }

        if( diProvider == null )
        {
            diProvider = ServiceCollection.BuildServiceProvider();
        }

        return diProvider.GetRequiredService<T>();
    }

    public static void RegisterMock(Type interfaceType, object mockObjectInstance)
    {
        if( !mockObjectInstance.GetType().IsInstanceOfType(interfaceType) )
        {
            throw new InvalidCastException($\"mockObjectInstance must be of type {interfaceType}\");
        }

        if( mockCollection.ContainsKey(interfaceType) )
        {
            mockCollection[interfaceType] = mockObjectInstance;
        }
        else
        {
            mockCollection.Add(interfaceType, mockObjectInstance);
        }
    }
}

测试相关代码

public interface ITestInterface
{
}

public class TestClass : ITestInterface
{
}

[Fact]
public void GetRequiredService_MoqObject_NormalOperation()
{
    Mock<ITestInterface> mock = new Mock<ITestInterface>();

    DiService.UseTestMode = true;
    DiService.RegisterMock(typeof(ITestInterface), mock.Object);

    ITestInterface testResult = DiService.GetRequiredService<ITestInterface>();

    Assert.NotNull(testResult);
}

这对于我自己很棒的模拟非常有用,但当我尝试使用 Moq 时却不行。为 Moq 对象抛出 InvalidCastException

RegisterMock 方法中,我无法弄清楚如何转换 Moq 对象以检查它是否实现了正确的接口。 Moq 对象实际上并没有从它所模拟的接口继承(接口是ITestInterfaceProxy)。我也无法弄清楚如何将对象转换为由变量指定的类型。

有没有办法在RegisterMock 中实际执行错误检查?

提前感谢您的回复。

附录: 根据 Peter Csala 的评论,我添加了这个示例,说明我如何使用上面的 DiService。我希望我没有将它简化到无用的地步。

[Fact]
public void TestLoad()
{
    DiService.UseTestMode = true;
    Mock<IDataAccess> mockDataAccess = new Mock<IDataAccess>();
    DiService.RegisterMock(typeof(IDataAccess), mockDataAccess.Object);
    Data testData = GenFu.A.New<Data>();

    mockDataAccess.Setup(mock => mock.LoadData(It.IsAny<string>()).Returns(testData);

    // Assuming that inside MyViewModel.LoadData there is some process that generates an IDataAccess and calls IDataAccess.LoadData
    MyViewModel viewModel = new MyViewModel();
    Data loadedData = viewModel.LoadData(\"some id\");

    Assert.Equal(testData, loadedData); 
}
  • 等什么???你想测试一个在正常模式和测试期间工作方式不同的类吗?为什么?
  • 好吧,当您测试一个注入了其依赖项的类时,模拟这些依赖项是很常见的。从这个意义上说,类的生产依赖项与测试的依赖项不同,所以是的,从技术上讲它是不同的。重点是测试除依赖关系之外的类中的逻辑。我上面所说的旨在提供在测试其他类时提供注入依赖项的方法,我只是想证明它可以通过单元测试工作。
  • 我还应该说,我还没有找到通过正常路由将 Moq 对象注入依赖注入服务的方法,因为 Moq 对象没有从它所模拟的接口继承。哦,DI 服务会自动创建所需对象的实例。我在测试时不希望这样。我想要一个可以操作的特定对象。

标签: c# casting moq


【解决方案1】:

只是为了澄清某些事情:

我正在尝试编写使用 Moq 将对象插入依赖注入服务的单元测试

如果进行单元测试,则不使用 DI.相反,您将模拟对象显式传递给正在测试的系统。例如:

你的系统被测类

private readonly IDependency dependency;
public SampleUsage(IDependency dep)
  => dependency = dep;

你的测试代码

//Arrange
Mock<IDependency> mockedDep = new Mock<IDependency>();
mockedDep.Setup(...).Returns(...);

var sut = SampleUsage(mockedDep.Object);

//Act
var actualResult = sut.XYZMethod();

在集成或端到端测试的情况下,您依赖 DI 系统,而不是通过用模拟的组件替换某些组件。相反,您正在使用不同的配置选项。

例如,如果您有一个与第一方或第三方 REST 服务通信的组件,那么您应该更改该服务的基地址配置。该新地址应指向本地运行的 http 模拟服务器。您可以使用 WireMock.NET 之类的库创建一个。

【讨论】:

    猜你喜欢
    • 2021-08-04
    • 1970-01-01
    • 2014-05-27
    • 1970-01-01
    • 2013-06-17
    • 2020-06-18
    • 1970-01-01
    • 1970-01-01
    • 2021-09-01
    相关资源
    最近更新 更多