模拟CloudStorageAccount和CloudTable用于Azure表存储

im9ewurl  于 2023-08-07  发布在  其他
关注(0)|答案(5)|浏览(95)

因此,我正在尝试测试Azure表存储并模拟我所依赖的东西。我的类是以一种我在构造函数中建立连接的方式来构建的,即。我创建了一个CloudStorageAccount的新示例,其中我创建了一个具有storageNamestorageKeyStorageCredentials示例。然后,我创建了一个CloudTable示例,在代码中进一步使用它来执行CRUD操作。我的类看起来如下:

public class AzureTableStorageService : ITableStorage
{
        private const string _records = "myTable";
        private CloudStorageAccount _storageAccount;
        private CloudTable _table;

        public AzureTableStorageService()
        {
            _storageAccount = new CloudStorageAccount(new StorageCredentials(
                 ConfigurationManager.azureTableStorageName, ConfigurationManager.azureTableStorageKey), true);
            _table = _storageAccount.CreateCloudTableClient().GetTableReference(_records);
            _table.CreateIfNotExistsAsync();
        }

        //...
        //Other methods here
}

字符串
_table在整个类中被重用以用于不同的目的。我的目标是模拟它,但由于它是虚拟的,并且没有实现任何接口,所以我无法提供简单的Mock解决方案,如:

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


因此,当我尝试以这种方式构建我的单元测试时,我得到:Type to mock must be an interface or an abstract or non-sealed class.
我的目标是完成这样的事情:

_table.Setup(x => x.DoSomething()).ReturnsAsync("My desired result");


任何想法都高度赞赏!

wqlqzqxt

wqlqzqxt1#

我还在为绑定到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
        });
    }
}

字符串
我通过传递存储模拟器用于本地存储的配置字符串来示例化mock类(参见https://learn.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函数。
也许这就是你要找的?

yhqotfr8

yhqotfr82#

为了补充答案,因为您的目标是使用mocking框架,只需设置一个从CloudTable继承并提供默认构造函数的对象,就可以让您Mock继承的对象本身并控制它返回的内容:

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;


(My Moq有点生疏,所以我猜上面的语法不太正确)

sy5wg1nm

sy5wg1nm3#

我遇到过与所选答案相同的场景,涉及具有表绑定的Azure函数。使用mock CloudTable有一些限制,特别是当使用System.LinqCreateQuery<T>时,例如,这些是IQueryable上的扩展方法。
更好的方法是使用HttpMessageHandler mock,比如 * RichardSzalay.MockHttp * 和TableClientConfiguration.RestExecutorConfiguration.DelegatingHandler,然后从表中删除您期望的json响应。

public class Azure_Function_Test_With_Table_Binding
{
    [Fact]
    public void Should_be_able_to_stub_out_a_CloudTable()
    {
        var storageAccount = StorageAccount.NewFromConnectionString("UseDevelopmentStorage=true");
        var client = storageAccount.CreateCloudTableClient();

        var mockedRequest = new MockHttpMessageHandler()
            .When("http://127.0.0.1:10002/devstoreaccount1/pizzas*")
            .Respond("application/json", 
            @"{
                ""value"": [
                    {
                        ""Name"": ""Pepperoni"",
                        ""Price"": 9.99
                    }
                ]
            }");

        client.TableClientConfiguration.RestExecutorConfiguration.DelegatingHandler = new MockedRequestAdapter(mockedRequest);
        var table = client.GetTableReference("pizzas");

        var request = new DefaultHttpContext().Request;
        request.Query = new QueryCollection(new Dictionary<string, StringValues> { { "Pizza", new StringValues("Pepperoni") } });

        var result = PizzaStore.Run(request, table, null);

        Assert.IsType<OkObjectResult>(result);
    }
}

public class MockedRequestAdapter : DelegatingHandler
{
    private readonly MockedRequest _mockedRequest;

    public MockedRequestAdapter(MockedRequest mockedRequest) : base()
    {
        _mockedRequest = mockedRequest;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        return await _mockedRequest.SendAsync(new HttpRequestMessage(request.Method, request.RequestUri), cancellationToken);
    }
}

public static class PizzaStore
{
    [FunctionName("PizzaStore")]
    public static IActionResult Run(
        [HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req,
        [Table("pizzas", Connection = "AzureWebJobsStorage")] CloudTable cloud,
        ILogger log)
    {
        if (req.Query.TryGetValue("Pizza", out var value))
        {
            var pizza = cloud.CreateQuery<Pizza>().Where(p => p.Name == value.ToString()).SingleOrDefault();

            return new OkObjectResult(new { Pizza = pizza.Name, Price = pizza.Price });
        }

        return new NotFoundResult();
    }
}

public class Pizza : TableEntity
{
    public string Name { get; set; }
    public double Price { get; set; }
}

字符串

cwdobuhd

cwdobuhd4#

下面是我的实现:

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;
    }

mklgxw1f

mklgxw1f5#

假设您使用的是Microsoft.WindowsAzure.Storage命名空间中的类,使用NSubstitute创建模拟相对容易,例如:

var mockStorageAccount = Substitute.For<CloudStorageAccount>(
  CloudStorageAccount.DevelopmentStorageAccount.Credentials,
  false);
var mockQueueClient = Substitute.For<CloudQueueClient>(
  mockStorageAccount.QueueStorageUri,
  mockStorageAccount.Credentials);

字符串
等等。

相关问题