c# - 使用 AutoFac 和 AutoMock 模拟 CloudBlobClient

标签 c# unit-testing moq autofac azure-blob-storage

我正在尝试为我的 AzureBlobRepository 编写单元测试。存储库在构造函数中接收一个 CloubBlobClient。我想模拟客户端,但这给出了一个异常(exception):

using (var mock = AutoMock.GetLoose())
{
    var mockClient = mock.Mock<CloudBlobClient>();
}

Cannot choose between multiple constructors with equal length 2 on type 'Microsoft.WindowsAzure.Storage.Blob.CloudBlobClient'. Select the constructor explicitly, with the UsingConstructor() configuration method, when the component is registered.

当然,在我的单元测试中我没有注册任何东西,所以这条消息不是很有帮助。

我还尝试了其他方法,例如提供 NameParameters、TypedParameters,或调用 mock.Create 代替 mock.Mock,但我尝试的所有方法都返回相同的异常消息。

(在CloudBlobContainer上也出现了同样的问题)

实现接口(interface)后更新 这是我编写的单元测试示例:

[TestMethod]
public void AzureBlobRepository_GetByIdAsync_ReturnsContent()
{    
    Guid blobId = Guid.NewGuid();
    Guid directoryName = Guid.NewGuid();
    string containerName = "unittest";

    using (var mock = AutoMock.GetLoose())
    {
        var mockClient = mock.Mock<ICloudBlobClient>();
        var mockContainer = mock.Mock<ICloudBlobContainer>();
        var mockDirectory = mock.Mock<ICloudBlobDirectory>();
        // notice that we're not using AutoMock here, it fails to create the mock
        var mockBlob = new Mock<CloudBlockBlob>(new Uri($"http://tempuri.org/{containerName}/{directoryName}/{blobId}"));
        mockBlob.Setup(m => m.DownloadTextAsync()).Returns(Task.FromResult("content"));

        mockClient.Setup(m => m.GetContainerReference(containerName))
            .Returns(mockContainer.Object);
        mockContainer.Setup(m => m.GetDirectoryReference(directoryName.ToString()))
            .Returns(mockDirectory.Object);
        mockDirectory.Setup(m => m.GetBlockBlobReference(blobId.ToString()))
            .Returns(mockBlob.Object);

        var repository = mock.Create<AzureBlobRepository>(
            new TypedParameter(typeof(ICloudBlobClient), mockClient.Object),
            new NamedParameter("container", containerName),
            new NamedParameter("directory", directoryName));

        var result = repository.GetByIdAsync(blobId, directoryName).Result;
        result.ShouldBe("content");
    }
}

最佳答案

这些类应被视为第 3 方实现问题。这意味着您无法控制它们,我们不应该 mock 我们无法控制的东西。它们应该封装在您可以控制的抽象之后,并且可以在单独测试时根据需要进行模拟。

public interface ICloudBlobClient {
    //...expose only the functionality I need
}

public class CloudBlobClientWrapper : ICloudBlobClient {
    private readonly CloudBlobClient client;

    public CloudBlobClientWrapper(CloudBlobClient client) {
        this.client = client;
    }

    //...implement interface wrapping
}

正是出于这个原因,类应该依赖于抽象而不是具体化。模拟具体类往往会产生链式 react

包装器不需要完全包装客户端,但可以聚合功能以免暴露实现问题。

所以现在当单独测试时你可以模拟你控制的抽象

using (var mock = AutoMock.GetLoose()) {
    var mockClient = mock.Mock<ICloudBlobClient>();

    /// ...and the rest of the test.
}

关于c# - 使用 AutoFac 和 AutoMock 模拟 CloudBlobClient,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45876042/

相关文章:

c# - 阅读 .Net 堆栈跟踪

javascript - 使用 FullCalendar、jQuery 和 MVC 查看数据库中的数据

c# - 从 Azure 函数访问嵌套 Json 设置

reactjs - React 测试库 - o​​jit_prehh 只能在 worker 内部使用

c# - .NET 核心 2.2 : xUnit Theory Inlinedata not working with enum values

c# - 使用最小起订量进行单元测试

mocking - 为什么接口(interface)首选模拟?

c# - LINQ,我应该加入还是使用嵌套的 SELECT NEW

dependency-injection - 使用 Moq 和 CaSTLe Windsor 进行部分模拟

需要身份验证服务的 GET 请求的 Angular Testing