Unit Testing a Retrieve TableOperation for Azure Table Storage - c#

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.

Related

Xunit - Moq always returns a null value even after setup

Hi I am having an issue with my moq when I try to return data, I saw this questtion with a solution but it doesn't work on my side.
Can anyone please tell me what I'm doing wrong
Here is my respository
private readonly IRepository<SomeClass<MyObject>> repository;
public MyObjectRepository(IRepositoryFactory factory)
{
repository = factory.RepositoryOf<SomeClass<MyObject>>();
}
public async Task<IEnumerable<MyObject>> GetAllAsync(string SomeParameter)
{
var result = await repository.GetAsync(x => x.OtherParameter == $"{SomeParameter}.{nameof(MyObject)}s", default);
var reportDataItem = result.FirstOrDefault();
if (reportDataItem == null)
return null;
return reportDataItem.Collection;
}
Here is my test
var data = await MockDBHelper.GetAirportsAsync(someParameter, true);
mockIRepository.Setup(x => x.GetAsync(null, default)).ReturnsAsync(data);
mockFactory.Setup(x => x.RepositoryOf<SomeClass<MyObject>>()).Returns(mockIRepository.Object);
_repo = new MyObjectRepository(mockFactory.Object);
var result = await _repo.GetAllAsync(AirlineCd);
mockRepository.Verify(x => x.GetAsync(null,default), Times.Once());
This statement
mockIRepository.Setup(x => x.GetAsync(null, default)).ReturnsAsync(data);
will only return data when the values of null and the default value are passed as the two parameters. Since you are passing a function as the first parameter it is not null, therefore not matching the setup. Instead you could tell mock to "Match anything" for the function.
mockIRepository.Setup(x => x.GetAsync(It.IsAny<Func<PARAMETER_TYPE, RETURN_TYPE>>(), default)).ReturnsAsync(data);
You will need to replace PARAMETER_TYPE and RETURN_TYPE with the appropriate types used in your code.

Setup with ReturnsAsync, need to return passed in value but getting null

I need to mock my IVehicleRecordsRepository for some tests. So here's the repo interface:
public interface IVehicleRecordsRepository
{
Task<VehicleRecord> StoreAsync(VehicleRecord veh, Guid? userId = null);
//...
}
and now I try to mock it in the xUnit test so StoreMethod() should return the same value that was passed in as a parameter. Here's the test that tests this scenario:
[Fact]
public async Task ShouldGetValueFromMockedMethod()
{
var mockRepo = new Mock<IVehicleRecordsRepository>();
mockRepo
.Setup(repo => repo.StoreAsync(It.IsAny<VehicleRecord>(), Guid.NewGuid()))
.ReturnsAsync((VehicleRecord veh, Guid userId) => veh)
// tried this too -> .Returns((VehicleRecord veh, Guid userId) => Task.FromResult(veh))
;
VehicleRecord vr = new VehicleRecord(newVehicle, Guid.NewGuid());
var testVeh = await mockRepo.Object.StoreAsync(vr);
Assert.NotNull(testVeh); // ====> FAILS HERE
Assert.AreEqual(vr, testVeh);
}
So, how can I get the same value I passed into StoreAsync() in return ?
Moq version: 4.7.99.0
I've not used Moq, so forgive my ignorance.
In your act statement:
var testVeh = await mockRepo.Object.StoreAsync(vr);
you're only passing in the Vehicle Record ('vr'), but your mocked object is set up to also expect a Guid.NewGuid(), correct?
mockRepo.Setup(repo => repo.StoreAsync(It.IsAny<VehicleRecord>(), Guid.NewGuid()))
Could it be that it's not matching the expectation and therefore, never calls the "(mockObject.)ReturnsAsync" method?
Have you tried doing something like:
var guid = Guid.NewGuid;
VehicleRecord vr = new VehicleRecord(newVehicle, guid);
var testVeh = await mockRepo.Object.StoreAsync(vr, guid);
Or, perhaps simplifying it a bit, change your mockObject to not expect a Guid, since you're passing in only the VehicleRecord:
mockRepo.Setup(repo => repo.StoreAsync(It.IsAny<VehicleRecord>()))

Moq cannot instantiate service that includes lambda expression

I want to mock the following method with Moq:
T GetOne(Expression<Func<T, bool>> expression);
which is called in the following method:
public GetTCNotifyFCResponse GetTCNotifyFC(string operationNumber)
{
var response = new GetTCNotifyFCResponse { IsValid = false };
try
{
var tcAbstract = _tcAbstractRepository
.GetOne(x => x.Operation.OperationNumber == operationNumber);
if (tcAbstract == null)
{
response.ErrorMessage = Localization.GetText(WORKFLOW_DONT_STARED);
return response;
}
[...]
The test code is:
var mockAbstractRep = new Mock<ITCAbstractRepository>();
mockAbstractRep
.Setup(s => s.GetOne(x => x.Operation.OperationNumber == operationNumber))
.Returns(entity);
but when running it I get a null "tcAbstract" result... "operationNumber" and "entity" variables are filled before and have not been included here for simplicity.
What am I doing wrong?
Try this and see if it helps
var mockAbstractRep = new Mock<ITCAbstractRepository>();
mockAbstractRep
.Setup(s => s.GetOne(It.IsAny<Expression<Func<EntityType, bool>>>()))
.Returns(entity);
replace EntityType with the type that your method requires

Mock Linq `Any` predicate with Moq

I'm trying to mock AnyAsync method in my repository with below codes but repository always returns false.
The signature of AnyAsync is:
Task<bool> AnyAsync<TEntity>(Expression<Func<TEntiry, bool>> predicate)
I tried the following setups:
1:
mockCustomerRepository.Setup(r => r.AnyAsync(c => c.Email == "some#one.com"))
.ReturnsAsync(true);
2:
Expression<Func<CustomerEntity, bool>> predicate = expr =>
expr.CustomerPerson.Email == "some#one.com";
mockCustomerRepository.Setup(r => r.AnyAsync(It.Is<Expression<Func<CustomerEntity, bool>>>
(criteria => criteria == predicate))).ReturnsAsync(true);
3:
mockCustomerRepository.Setup(r => r.AnyAsync(It.IsAny<Expression<Func<CustomerEntity, bool>>>()))
.ReturnsAsync(true);
My test:
public class Test
{
Mock<ICustomerRepository> mockCustomerRepository;
public Test()
{
mockCustomerRepository = new Mock<ICustomerRepository>();
}
[Fact]
public async Task CustomerTest()
{
var customer = ObjectFactory.CreateCustomer(email: "some#one.com");
var sut = new CustomerService(mockCustomerRepository.Object);
var result = await sut.ValidateCustomerAsync(customer);
.
.
.
}
}
My CustomerService.ValidateCustomerAsync method:
public async Task<OperationResult> ValidateCustomerAsync(CustomerEntity customer)
{
var errors = new List<ValidationResult>();
if (await _repository.AnyAsync(c => c.Email == customer.Email))
errors.Add(new ValidationResult("blah blah")));
I've also read this but it doesn't work too.
The following snippet shows the correct way to mock your AnyAsync method:
[TestMethod]
public async Task TestMethod1()
{
var fakeCustomerRepo = new Mock<ICustomerRepository>();
var foo = false;
fakeCustomerRepo.Setup(repository => repository.AnyAsync(It.IsAny<Expression<Func<CustomerEntity, bool>>>()))
.Callback<Expression<Func<CustomerEntity, bool>>>(
expression =>
{
var func = expression.Compile();
foo = func(new CustomerEntity() {Email = "foo#gmail.com"});
})
.Returns(() => Task.FromResult(foo));
var customer = new CustomerEntity() {Email = "foo#gmail.com"};
var result = await fakeCustomerRepo.Object.AnyAsync<CustomerEntity>(c => c.Email == customer.Email);
Assert.IsTrue(result);
customer = new CustomerEntity() { Email = "boo#gmail.com" };
result = await fakeCustomerRepo.Object.AnyAsync<CustomerEntity>(c => c.Email == customer.Email);
Assert.IsFalse(result);
}
using the above setup you can verify your predicate which is part of your unit behavior.
I think you're running into the difficulties of matching predicates. Funcs or expressions of Funcs use reference-equality, so just using == to compare the two instances isn't going to work. (As a general rule, if you can't get predicate1.Equals(predicate2) to return true, Moq's argument matchers aren't going to match.)
This is a little unorthodox, but I refer you to my answer to a similar question for FakeItEasy matchers, which in turn points you at a number of techniques for validating predicates.
All I can see that should cause this problem are two options:
1. The repository method does not come from an interface that you should mock to return the desired value, as it is not marked with virtual.
2. The types (TEntity) you use in method do not match when you are using the moq.
AnyAsync<TEntity>(Expression<Func<TEntity, bool>>
as you can setup for let's say MemberEntity and call the ValidateCustomerAsync with a CustomerEntity.
Maybe I am wrong but, but as far as I have read for methods that return only a Task, .Returns(Task.FromResult(default(object))) can and should be used.
So in your case it would be mocksCustomerRepository.Setup(r => r.AnyAsync(c => c.Email == "some#one.com")).Returns(Task.FromResult(true));

Unit testing LINQ which returns an integer

I have this test which verifies if I call the Query() in my reference.
[TestFixture]
public class When_retrieving_an_application_license
{
[Test]
public void available_licenses_should_be_counted()
{
// Arrange
var sut = new LicenseManager();
var mockILicenseRepository = new Mock<ILicenseRepository>();
sut.LicenseRepository = mockILicenseRepository.Object;
// Act
sut.GetLicenseCount(Guid.NewGuid(), Guid.NewGuid());
// Assert
mockIApplicationLicenseRepository.Verify(x => x.Query());
}
}
However, the GetLicenseCount(Guid.NewGuid(), Guid.NewGuid()) function looks like this:
public int GetLicenseCount(Guid cId, Guid appId)
=> LicenseRepository.Query()
.Count(al => al.CId == cId && al.AppId == appId
&& al.UserId == null
&& al.Expiry > DateTime.UtcNow);
Query() returns all in the repo to count which UserId's are in null.
Is it enough to say that the test is OK even if it only verifies the query() part of the linq?
How about the count?
If you are wanting to make sure the right count is being returned you will need to arrange your mocked ILicenseRepository to return the data you are expecting.
var mockILicenseRepository = new Mock<ILicenseRepository>();
mockILicenseRepository.Setup(x => x.Query()).Returns(new {CId = guid, AppId = guid2, Expiry = DateTime.Now.AddDays(1)};
You will also need to store the return value to assert against
var actualCount = sut.GetLicenseCount(Guid.NewGuid(), Guid.NewGuid());
Assert.AreEqual(expectedCount, actualCount);
However, if you are unit testing LINQ's Query method then I think you may want to restructure your production code because unit testing framework methods is unnecessary.

Categories