模拟CloudStorageAccount和CloudTable for Azure表存储

时间:2018-11-27 23:49:26

标签: c# azure unit-testing mocking azure-table-storage

因此,我尝试测试Azure表存储并模拟我依赖的东西。我的类的构造方式是在构造函数中建立连接,即,我创建了CloudStorageAccount的新实例,在其中创建了具有StorageCredentials和{{ 1}}。之后,我创建一个storageName的实例,在代码中进一步使用它来执行CRUD操作。我的课如下:

storageKey

CloudTable在整个类中出于不同目的被重用。我的目标是模拟它,但是由于它是虚拟的并且没有实现任何接口,因此我无法提供简单的public class TableStorage : ITableStorage { private const string _records = "myTable"; private CloudStorageAccount _storageAccount; private CloudTable _table; private ILogger<TableStorage> _logger; public AzureTableStorageService(ILogger<TableStorage> loggingService) { _storageAccount = new CloudStorageAccount(new StorageCredentials( ConfigurationManager.azureTableStorageName, ConfigurationManager.azureTableStorageKey), true); _table = _storageAccount.CreateCloudTableClient().GetTableReference(_records); _table.CreateIfNotExistsAsync(); _logger = loggingService; } //... //Other methods here } 解决方案,例如:

_table

因此,当我尝试以这种方式构造单元测试时,我得到: Mock

我的目标是完成类似的事情:

_storageAccount = new Mock<CloudStorageAccount>(new Mock<StorageCredentials>(("dummy", "dummy"), true));
_table  = new Mock<CloudTable>(_storageAccount.Object.CreateCloudTableClient().GetTableReference(_records));

任何想法都受到高度赞赏!

4 个答案:

答案 0 :(得分:2)

我还努力为Azure功能实现单元测试,并绑定到Azure表存储。我终于使用派生的CloudTable类工作了,可以覆盖我使用的方法并返回固定的结果。

/// <summary>
/// Mock class for CloudTable object
/// </summary>
public class MockCloudTable : CloudTable
{

    public MockCloudTable(Uri tableAddress) : base(tableAddress)
    { }

    public MockCloudTable(StorageUri tableAddress, StorageCredentials credentials) : base(tableAddress, credentials)
    { }

    public MockCloudTable(Uri tableAbsoluteUri, StorageCredentials credentials) : base(tableAbsoluteUri, credentials)
    { }

    public async override Task<TableResult> ExecuteAsync(TableOperation operation)
    {
        return await Task.FromResult(new TableResult
        {
            Result = new ScreenSettingEntity() { Settings = "" },
            HttpStatusCode = 200
        });
    }
}

我通过传递存储模拟器用于本地存储的配置字符串来实例化模拟类(请参见https://docs.microsoft.com/en-us/azure/storage/common/storage-configure-connection-string)。

var mockTable = new MockCloudTable(new Uri("http://127.0.0.1:10002/devstoreaccount1/screenSettings"));

在此示例中,“ screenSettings”是表格的名称。

现在可以将模拟类从单元测试传递给Azure函数。

也许这就是您想要的?

答案 1 :(得分:1)

我遇到了与所选答案相同的情况,涉及带有表绑定的 Azure 函数。使用模拟 $ helm install myapp myapp --set environment=sandbox apiVersion: apps/v1 kind: Deployment metadata: ... spec: {{- if not .Values.autoscaling.enabled }} # In pseudo-code, in my YAML files # Get the index value from .Values.environments list # based on pass-in environment parameter {{ $myIndex = indexOf .Values.environments .Value.environment }} replicas: {{ .Values.perClusterValues.replicas $myIndex }} {{- end }} 有一些限制,尤其是在将 CloudTableSystem.Linq 一起使用时,例如这些是 CreateQuery<T> 上的扩展方法。

更好的方法是使用 IQueryable 模拟,例如 RichardSzalay.MockHttpHttpMessageHandler,然后仅删除您期望从表中获得的 json 响应。

TableClientConfiguration.RestExecutorConfiguration.DelegatingHandler

答案 2 :(得分:0)

要在此处添加答案,因为您打算使用模拟框架,只需设置一个从CloudTable继承并提供默认构造函数的对象,即可让您模拟继承的对象本身并控制其返回的内容:

public class CloudTableMock : CloudTable
{
    public CloudTableMock() : base(new Uri("http://127.0.0.1:10002/devstoreaccount1/screenSettings"))
    {
    }
}

这只是创建模拟的一种情况。我正在使用NSubstitute,所以我这样做了:

_mockTable = Substitute.For<CloudTableMock>();

但我猜测Moq会允许:

_mockTableRef = new Mock<CloudTable>();
_mockTableRef.Setup(x => x.DoSomething()).ReturnsAsync("My desired result");
_mockTable = _mockTableRef.Object;

(我的起订量有点生锈,所以我猜上面的语法不太正确)

答案 3 :(得分:0)

这是我的实现方式

 public class StorageServiceTest
{
   IStorageService _storageService;
    Mock<CloudStorageAccount> _storageAccount;
    [SetUp]
    public void Setup()
    {
        var c = new StorageCredentials("dummyStorageAccountName","DummyKey");
       _storageAccount = new Mock<CloudStorageAccount>(c, true);
        _storageService = new StorageService(_storageAccount.Object);
    }

    [Test]
    [TestCase("ax0-1s", "random-1")]
    public void get_content_unauthorized(string containerName,string blobName)
    {
        //Arrange
        string expectOutputText = "Something on the expected blob";
        Uri uri = new Uri("https://somethig.com//");
        var blobClientMock = new Mock<CloudBlobClient>(uri);
        _storageAccount.Setup(a => a.CreateCloudBlobClient()).Returns(blobClientMock.Object);

        var cloudBlobContainerMock = new Mock<CloudBlobContainer>(uri);
        blobClientMock.Setup(a => a.GetContainerReference(containerName)).Returns(cloudBlobContainerMock.Object);

        var cloudBlockBlobMock = new Mock<CloudBlockBlob>(uri);
        cloudBlobContainerMock.Setup(a => a.GetBlockBlobReference(blobName)).Returns(cloudBlockBlobMock.Object);

        cloudBlockBlobMock.Setup(a => a.DownloadTextAsync()).Returns(Task.FromResult(expectOutputText));

        //Act
       var actual = _storageService.DownloadBlobAsString(containerName, blobName);

        //Assert
        Assert.IsNotNull(actual);
        Assert.IsFalse(string.IsNullOrWhiteSpace(actual.Result));
        Assert.AreEqual(actual.Result, expectOutputText);
    }
}

服务类实现:

 Task<string> IStorageService.DownloadBlobAsString(string containerName, string blobName)
    {
        var blobClient = this.StorageAccountClient.CreateCloudBlobClient();

        var blobContainer = blobClient.GetContainerReference(containerName);

        var blobReference = blobContainer.GetBlockBlobReference(blobName);

        var blobContentAsString = blobReference.DownloadTextAsync();

        return blobContentAsString;
    }