简体   繁体   中英

Unit Testing a Retrieve TableOperation for Azure Table Storage

I have some code that I'd like to unit test.
I am retrieving some data from an Azure Storage Table database as part of my method that I'd like to test, so I need to mock out the return from the database.

Code to test:

public class GetCustomer : IGetCustomer
{
   //constructor
   public GetCustomer(IClientTableFactory clientTableFactory)
   {
      _partitionKey = "test";
      _customersTable = clientTableFactory.GetStorageTable("customers");
   }

   //method to test
   public async Task<string> GetCustomerNameAsync(string email)
   {
      //match on full email
      var match = await SearchAsync(email.ToLower());
      if (match == null)
      {
         //just match on email domain
         var domain = email.ToLower().Substring(email.IndexOf("@"));
         match = await SearchAsync(domain);
      }
      return match;
   }

   //internal method that queries Azure Table Storage
   private async Task<string> SearchAsync(string searchString)
   {
      var query = TableOperation.Retrieve<Customer>(_partitionKey, rowkey: searchString);
      var result = await _customersTable.ExecuteAsync(query);
      var match = result.Result as Customer;
      return match?.Name;
   }
}

Unit Test so far:

//Arrange
var email = "Testy.McTest@Test.com.au";
var tableFactory = new Mock<IClientTableFactory>();
var customersTable = new Mock<CloudTable>(new Uri("http://unittests.localhost.com/FakeTable"));
customersTable.Setup(x => x.ExecuteAsync(It.IsAny<TableOperation>()))
   .ReturnsAsync(new TableResult{ HttpStatusCode = 200, Result = new Customer{ Name = "jiminy crickets" }});
tableFactory.Setup(x => x.GetStorageTable("customers")).Returns(customersTable.Object);
var getCustomers = new GetCustomer(tableFactory.Object);
// Act
var result = await getCustomers.GetCustomerNameAsync(email);
// Assert
Assert.AreEqual("jiminy crickets", result);

Of course, the test passes every time. The missing piece of the puzzle that I would like to mock out is this line:

customersTable
    .Setup(x => x.ExecuteAsync(It.IsAny<TableOperation>()))
    .ReturnsAsync...

I should be able to replace It.IsAny<TableOperation>() with my search query eg It.Is<TableOperation>(y => y.RowKey == "testy.mctest@test.com.au" but unfortunately RowKey is inaccessible.

I've also tried

customersTable
    .Setup(x => x.ExecuteAsync(TableOperation.Retrieve<Customer>(_partitionKey, rowkey: searchString))

but it never passes this code at runtime - maybe because of the ETag property?

Any ideas? I've seen plenty of answers about mocking Table Storage, but none about mocking Query results.

If you don't mind using reflection you can find the values in the internal members "RetrievePartitionKey" and "RetrieveRowKey".

I wrote a simple method:

  private T GetInternalMember<T>(object obj, string propertyName)
    {
        Type objType = obj.GetType();
        PropertyInfo propInfo = objType.GetProperty(propertyName,
                BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

        return (T)propInfo.GetValue(obj, null);
    }

And I'm using it like:

cloudTable.Verify(x => x.ExecuteAsync(It.Is<TableOperation>(op => GetInternalMember<string>(op, "RetrievePartitionKey") == "TestPartitionKey"
                                                                           && GetInternalMember<string>(op, "RetrieveRowKey") == "TestRowKey")));

It's not really ideal, but it works for unit tests.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM