本文介绍了使用AutoFac和AutoMock模拟CloudBlobClient的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为AzureBlobRepository编写单元测试.该存储库在构造函数中接收一个CloubBlobClient.我想模拟客户端,但这给出了一个例外:

I'm trying to write unit tests for my AzureBlobRepository. The repository receives a CloubBlobClient in the constructor. I want to mock the client, but this gives an exception:

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

当然,在我的单元测试中,我没有注册任何东西,因此该消息不是很有帮助.

Ofcourse, in my unit test I am not registering anything, so that message is not very helpful.

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

I also tried other ways like providing NameParameters, TypedParameters, or calling mock.Create in stead of mock.Mock, but everything I try returns the same exception message.

(CloudBlobContainer上也会发生相同的问题)

(same problem also occurs on CloudBlobContainer)

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

UPDATE after implementing interfaceshere is an example of a unit test that I wrote:

[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");
    }
}

推荐答案

这些类应被视为第三方实现的关注点.这意味着您无法控制它们,并且我们不应该嘲笑我们无法控制的事物.它们应该封装在您可以控制的抽象后面,并且在隔离测试时可以根据需要进行模拟.

Those classes should be treated as 3rd party implementation concerns. This means that you have no control over them and we should not mock what we have no control over. They should be encapsulated behind abstractions that you do control and can mock as needed when testing in isolation.

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
}

出于这个原因,类应该依赖于抽象而不是依赖于具体.模拟具体课程往往会产生影响

Classes should depend on abstraction and not concretions for that very reason. Mocking concrete classes tend to have knock on effects

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

The wrapper does not need to wrap the client exactly but can aggregate functionality so as not to expose implementation concerns.

所以现在当进行隔离测试时,您可以模拟您控制的抽象

So now when testing in isolation you can mock the abstraction you control

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

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

这篇关于使用AutoFac和AutoMock模拟CloudBlobClient的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-29 22:29