对 Azure Table 存储的检索 Table 操作进行单元测试
Unit Testing a Retrieve TableOperation for Azure Table Storage
我有一些代码想要进行单元测试。
作为我想要测试的方法的一部分,我正在从 Azure 存储 Table 数据库中检索一些数据,因此我需要从数据库中模拟出 return。
要测试的代码:
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;
}
}
到目前为止的单元测试:
//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);
当然,每次都通过测试。我想模拟的拼图缺失的部分是这一行:
customersTable
.Setup(x => x.ExecuteAsync(It.IsAny<TableOperation>()))
.ReturnsAsync...
我应该可以用我的搜索查询替换 It.IsAny<TableOperation>()
,例如 It.Is<TableOperation>(y => y.RowKey == "testy.mctest@test.com.au"
,但不幸的是 RowKey 无法访问。
我也试过了
customersTable
.Setup(x => x.ExecuteAsync(TableOperation.Retrieve<Customer>(_partitionKey, rowkey: searchString))
但它从不在运行时传递此代码 - 可能是因为 ETag
属性?
有什么想法吗?我已经看到很多关于模拟 Table 存储的答案,但是 none 关于模拟查询结果的答案。
如果您不介意使用反射,您可以在内部成员“RetrievePartitionKey”和“RetrieveRowKey”中找到值。
我写了一个简单的方法:
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);
}
我使用它的方式如下:
cloudTable.Verify(x => x.ExecuteAsync(It.Is<TableOperation>(op => GetInternalMember<string>(op, "RetrievePartitionKey") == "TestPartitionKey"
&& GetInternalMember<string>(op, "RetrieveRowKey") == "TestRowKey")));
它不是很理想,但它适用于单元测试。
我有一些代码想要进行单元测试。
作为我想要测试的方法的一部分,我正在从 Azure 存储 Table 数据库中检索一些数据,因此我需要从数据库中模拟出 return。
要测试的代码:
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;
}
}
到目前为止的单元测试:
//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);
当然,每次都通过测试。我想模拟的拼图缺失的部分是这一行:
customersTable
.Setup(x => x.ExecuteAsync(It.IsAny<TableOperation>()))
.ReturnsAsync...
我应该可以用我的搜索查询替换 It.IsAny<TableOperation>()
,例如 It.Is<TableOperation>(y => y.RowKey == "testy.mctest@test.com.au"
,但不幸的是 RowKey 无法访问。
我也试过了
customersTable
.Setup(x => x.ExecuteAsync(TableOperation.Retrieve<Customer>(_partitionKey, rowkey: searchString))
但它从不在运行时传递此代码 - 可能是因为 ETag
属性?
有什么想法吗?我已经看到很多关于模拟 Table 存储的答案,但是 none 关于模拟查询结果的答案。
如果您不介意使用反射,您可以在内部成员“RetrievePartitionKey”和“RetrieveRowKey”中找到值。
我写了一个简单的方法:
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);
}
我使用它的方式如下:
cloudTable.Verify(x => x.ExecuteAsync(It.Is<TableOperation>(op => GetInternalMember<string>(op, "RetrievePartitionKey") == "TestPartitionKey"
&& GetInternalMember<string>(op, "RetrieveRowKey") == "TestRowKey")));
它不是很理想,但它适用于单元测试。