XUnit how to mock IMemoryCache ASP.NET Core - c#

I understand IMemoryCache.Set is an extension method so it can not be mocked. People have provided workarounds to such situation e.g as one by the NKosi here. I am wondering how I can achieve that for my data access layer where my MemoryCache returns a value and when not found it gets data from the db, set it to the MemoryCache and return the required value.
public string GetMessage(int code)
{
if(myMemoryCache.Get("Key") != null)
{
var messages= myMemoryCache.Get<IEnumerable<MyModel>>("Key");
return messages.Where(x => x.Code == code).FirstOrDefault().Message;
}
using (var connection = dbFactory.CreateConnection())
{
var cacheOptions = new MemoryCacheEntryOptions { SlidingExpiration = TimeSpan.FromHours(1) };
const string sql = #"SELECT Code, Message FROM MyTable";
var keyPairValueData = connection.Query<KeyPairValueData>(sql);
myMemoryCache.Set("Key", keyPairValueData, cacheOptions );
return keyPairValueData.Where(x => x.Code == code).FirstOrDefault().Message;
}
}
Following is my Unit Test - And off course it is not working as I can't mock IMemoryCache
[Fact]
public void GetMessage_ReturnsString()
{
//Arrange
// Inserting some data here to the InMemoryDB
var memoryCacheMock = new Mock<IMemoryCache>();
//Act
var result = new DataService(dbConnectionFactoryMock.Object, memoryCacheMock.Object).GetMessage(1000);
//assert xunit
Assert.Equal("Some message", result);
}

The first thing I would say is why not use a real memory cache? It would verify the behavior much better and there's no need to mock it:
// Arrange
var memCache = new MemoryCache("name", new NameValueCollection());
//Act
var result = new DataService(dbConnectionFactoryMock.Object, memCache).GetMessage(1000);
// Assert: has been added to cache
memCache.TryGetValue("Key", out var result2);
Assert.Equal("Some message", result2);
// Assert: value is returned
Assert.Equal("Some message", result);
If you really want to mock it out, here's a guide on how to do that:
Because it's an extension method, you need to make sure that it can be called as is. What happens in your case is that the extension method will call into the mock. Since you provide no expected behavior, it will probably fail.
You need to look at the code for the extension method, check what it accesses and then ensure that your mock complies with the expected behavior. The code is available here:
https://github.com/aspnet/Caching/blob/master/src/Microsoft.Extensions.Caching.Abstractions/MemoryCacheExtensions.cs#L77
This is the code:
public static TItem Set<TItem>(this IMemoryCache cache, object key, TItem value, MemoryCacheEntryOptions options)
{
using (var entry = cache.CreateEntry(key))
{
if (options != null)
{
entry.SetOptions(options);
}
entry.Value = value;
}
return value;
}
So, from that, you can see that it accesses CreateEntyand expects an object from it. Then it calls SetOptions and assigns Value on the entry.
You could mock it like this:
var entryMock = new Mock<ICacheEntry>();
memoryCacheMock.Setup(m => m.CreateEntry(It.IsAny<object>())
.Returns(entryMock.Object);
// maybe not needed
entryMock.Setup(e => e.SetOptions(It.IsAny<MemoryCacheEntryOptions>())
...
When you do this, the extension method will be called on the mock and it will return the mocked entry. You can modify the implementation and make it do whatever you want.

Related

How do I get a result from this mocked service?

How do I get a result from a mocked service? Note: It works properly. I'm just trying to get the test going.
The service:
public interface ISendgridService
{
Task<Response> SendAsync(IEmailMessage emailMessage);
}
// The test
[TestMethod]
public async Task SendEmailTest()
{
// Arrange
var mockSendgrid = new Mock<ISendgridService>();
var response = new Mock<Func<SendGrid.Response>>();
mockSendgrid.Setup(s => s.SendAsync(It.IsAny<IEmailMessage>()))
.ReturnsAsync(response.Object.Invoke);
var emailMessage = _builder.CreateNew<EmailMessage>()
.With(e => e.From = _sendgridConfiguration.SenderEmail)
.With(e => e.FromName = _sendgridConfiguration.SenderName)
.With(e => e.To = Faker.Internet.Email())
.Build();
// Act
var result = await mockSendgrid.Object.SendAsync(emailMessage);
// Assert
// result is null // How do I get a value?
}
I am a little confused as to what you're trying to test. Usually, you'd act on a concrete class under test, not a stubbed object.
Pseudo code below:
public class SendGridService : ISendGridService {
public async Task<bool> SendAsync() {
//code that sends the email
}
}
Your test would be like:
//Act
var subject = new SendGridService();
var result = await subject.SendAsync();
//Assert
Assert.IsTrue(result);
You've set up the Mock<ISendgridService> to return something - it uses another mock - Mock<Func<SendGrid.Response>> and returns the result of invoking that Func.
But you haven't set up the Mock<Func<SendGrid.Response>> to return anything, so it doesn't.
In other words, when you do this:
mockSendgrid.Setup(s => s.SendAsync(It.IsAny<IEmailMessage>()))
.ReturnsAsync(response.Object.Invoke);
What does response.Object.Invoke return? response.Object is a Mock<Func<SendGrid.Response>>, but it hasn't been set up to return anything.
A Func is actually much easier to mock without using Moq. You can just do this:
// Create the thing you need the function to return, whatever that looks like.
var response = new SendGrid.Response(someStatusCode, body, headers);
// create a function that returns it.
var functionMock = new Func<SendGrid.Response>(() => response);
That depends on what you want to test of the app, if you are trying to test whether it sends an email or not, you shouldnt be using a mock class if you want to actually test if it sends. What you should mock is the content of the email.
Now if what you want to check is that certain steps including the email works (integrating all of them in a method) thats another matter, in this case you should use a dummy task that returns a dummy response.

MOQ testing Email Service and unable to get back the value

I am trying to test a Domain Service which is to send email after the order has been placed. This service has private methods so I called a method on the public interface which was calling this private service method. Issue is I cant seem to check CC on the email as this is in the private method.
the only approach I know to figure this if that value was saved as an interface property etc but its not. see the code below.
public int SendConsolidatedDespatchNotifications(int countOfWorkDays)
{
var sent = 0;
var trackings = _despatchLineRepository.GetTrackingWithoutDespatchNotificationInPreviousWorkDays(countOfWorkDays);
var trackingsWithinOrder = trackings == null
? new List<OrderLineTracking>()
: trackings.Where(dl => dl.DespatchReference != null).ToList();
trackingsWithinOrder.GroupBy(ot => ot.OrderKey).ForEach(
ot =>
{
if (SendConsolidatedDespatchNotifications(ot))
{
_despatchLineRepository.SetAsSent(ot.Select(ol => ol.DespatchLine));
sent++;
}
});
return sent;
}
private bool SendConsolidatedDespatchNotifications(IGrouping<int, OrderLineTracking> orderTrackingLines)
{
if (orderTrackingLines == null)
return false;
if (orderTrackingLines.Key == 0)
return false;
if (orderTrackingLines.Any())
{
var firstLine = orderTrackingLines.First();
var allOrderLines = _orderLineRepository.GetOrderLinesByOrderKey(firstLine.OrderKey);
var partiallyDespatchedLines = FindPartiallyDespatchedLines(orderTrackingLines);
var notDespatchedLines = FindNotDespatchedLines(allOrderLines, orderTrackingLines);
return SendConsolidatedDespatchedEmail(firstLine.DespatchReference, orderTrackingLines, partiallyDespatchedLines, notDespatchedLines);
}
return false;
}
private bool SendConsolidatedDespatchedEmail(
string poNumber,
IEnumerable<OrderLineTracking> despatchedLines,
IEnumerable<OrderLineTracking> partiallyDespatchedLines,
IEnumerable<OrderLine> notDespatchLines)
{
//we just assume that one PO have always just one order
var firstDespatchedLine = despatchedLines.First();
var order = firstDespatchedLine.OrderLine.OrderHeader;
if (order?.Customer == null)
return false;
var despatchGroups = new List<DespatchLineGroup>();
despatchedLines.GroupBy(dl => dl.DespatchReference).ForEach(
dl => despatchGroups.Add(
new DespatchLineGroup
{
DespatchReference = dl.Key,
DespatchedLines = dl,
TrackingWebLink = GetTrackingWebLinkFor(dl.First())
}));
var despatchNotificationEmail = new DespatchConsolidatedNotificationEmail(
order.Customer,
order,
despatchGroups,
CreateNotDespatchedItemsList(partiallyDespatchedLines, notDespatchLines));
var ccCustomer = _customerRepository.GetByCostCentreIdentifier(order.CostCentreIdentifier, order.Customer.Key);
var ccOnBasket = ccCustomer?.CostCentre;
if (ccOnBasket == null)
{
despatchNotificationEmail.To.Add(new EmailAddress(order.Customer.FullName, order.Customer.Login));
}
else
{
FillInSubaccountDetails(despatchNotificationEmail, ccCustomer, order, order.Customer, ccOnBasket);
}
despatchNotificationEmail.PopulateContentWithTags();
despatchNotificationEmail.SendAfter = firstDespatchedLine.DespatchDate;
despatchNotificationEmail.Save();
_log.InfoFormat("Despatch email {0} for {2} sent to {1}", "DespatchConsolidatedNotificationEmail", order.Customer.Login, poNumber);
return true;
}
private void FillInSubaccountDetails(
EmailTemplate email,
Customer ccCustomer,
OrderHeader order,
Customer masterAccount,
CostCentre ccOnBasket)
{
//send notifications to CostCentre account, which is on basket
email.To.Add(new EmailAddress(ccCustomer.FullName, ccCustomer.Login));
if (ccOnBasket.ReceiveNotifications) //send notifications to master only if CC is set so
{
email.To.Add(new EmailAddress(masterAccount.FullName, masterAccount.Login));
}
if (order.OrderPlacedBy.HasValue) //PD-2140 Sending email to Purchaser as well
{
var purchaser = _customerRepository.Get(order.OrderPlacedBy.Value);
if (purchaser?.Purchaser != null && purchaser.Purchaser.ReceiveNotifications)
{
email.To.Add(new EmailAddress(purchaser.FullName, purchaser.Login));
}
}
if ( order.ApprovedBy != null)
{
var approver = _customerRepository.Get(order.ApprovedBy.Value);
if(approver?.Approver != null && //has approver and its not MAH
approver.Approver.ReceiveNotifications)
email.To.Add(new EmailAddress(approver.FullName, approver.Login));
}
}
//this inherits from EmailTemplate which has save method.
public class DespatchConsolidatedNotificationEmail : EmailTemplate
{
public DespatchConsolidatedNotificationEmail() { }
public DespatchConsolidatedNotificationEmail(
Customer customer,
OrderHeader orderHeader,
List<DespatchLineGroup> despatchLines,
List<NotDespatchedLine> notDespatchLines)
{
AddEmailData(customer);
AddEmailData(orderHeader);
AddEmailData(despatchLines);
AddEmailData(notDespatchLines);
}
}
//below is the save method
public int Save()
{
var manageSave = Configuration.Container.Resolve<IWantToManageSaving>();
return manageSave.Save(this);
}
Note Email implements a abstract class which is EmailTemplate not an interface..
I want to figure out which emailAddress has been added ?
There are arguments pro and con unit testing private methods. I'll leave it up to you to decide if it's a good idea or not. Having said that you can use the PrivateObject class. Something along these lines:
Class c = new Class();
PrivateObject o = new PrivateObject(c);
var whatever = o.Invoke("FillInSubaccountDetails");
Assert.AreEqual(whatever, expected);
There is a problem here since your method returns void, there's no return value to assert. You may need to adapt your method?
So, based on all the code you provided:
Your Save method smells of bad practice. You shouldn't use an IoC container to manually resolve dependencies. If you had the IWantToManageSaving (I like the naming by the way :) ) injected via a ctor you could mock it in your test. If you had a var savingManagerMock = new Mock<IWantToManageSaving>(), you could then verify in your unit test that the Save method was called with a correctly setup instance of EmailTemplate. Something like:
// ASSERT
savingManagerMock.Verify(x => x.Save(It.IsAny<EmailTemplate>(
arg => arg.To.Contains(/* ... */));
Or something or the like, depends on the actual assertions you want.
Another way would be to abstract the construction of the DespatchConsolidatedNotificationEmail into a factory IDespatchConsolidatedNotificationEmailFactory, make it return a special mock of DespatchConsolidatedNotificationEmail and setup its Save method to, for example, save the current state of the EmailTemplate and then assert it. I would still lean towards the first solution, however.
On an ending note: as you can see testing this method is fairly complicated. That usually means it could be written better. In this case I see two red flags, first one is the explicit use of a Container which can always be avoided with dependency injection (manual resolution ain't no injection :P). Second is that this method is fairly complicated! It calls a lot of private methods and there's a lot of logic that can't be understood by a quick read through the method. You should consider splitting these private methods into maybe internal helper methods in another class that would be unit-tested separately. Then you could trust them when testing this public method, as they're basicaly a dependency at that point, setup mocks and just assert that correct internal methods are called and the method's contract is fulfilled.

Is it a good practice to mock Automapper in unit tests?

There is this codebase where we use automapper and have 2 layers, Domain and Service. Each has its object for data representation, DomainItem and ServiceItem. The service gets data from domain, the uses constructor injected automapper instance to map
class Service
{
public ServiceItem Get(int id)
{
var domainItem = this.domain.Get(id);
return this.mapper.Map<DomainItem, ServiceItem>(domainItem);
}
}
Assume best practices, so mapper has no side-effects and no external dependencies. You'd write a static function to convert one object to another within seconds, just mapping fields.
With this in mind, is it a good practice to mock the mapper in unit tests like this?
[TestClass]
class UnitTests
{
[TestMethod]
public void Test()
{
var expected = new ServiceItem();
var mockDomain = new Mock<IDomain>();
// ... setup
var mockMapper = new Mock<IMapper>();
mockMapper.Setup(x => x.Map<DomainItem, ServiceItem>(It.IsAny<DomainItem>()))
.Returns(expected);
var service = new Service(mockDomain.Object, mockMapper.Object);
var result = service.Get(0);
Assert.AreEqual(expected, result);
}
}
To me, it seems that such unit test does not really bring any value, because it is effectively testing only the mocks, So i'd either not write it at all OR I'd use the actual mapper, not the mocked one. Am I right or do I overlook something?
I think the issue here is that the test is badly written for what it is actually trying to achieve which is testing Service.Get().
The way I would write this test is as follows:
[TestMethod]
public void Test()
{
var expected = new ServiceItem();
var mockDomain = new Mock<IDomain>();
var expectedDomainReturn = new DomainItem(0); //Illustrative purposes only
mockDomain.Setup(x => x.DomainCall(0)).Returns(expectedDomainReturn); //Illustrative purposes only
var mockMapper = new Mock<IMapper>();
mockMapper.Setup(x => x.Map<DomainItem, ServiceItem>(It.IsAny<DomainItem>()))
.Returns(expected);
var service = new Service(mockDomain.Object, mockMapper.Object);
var result = service.Get(0);
mockDomain.Verify(x => x.DomainCall(0), Times.Once);
mockMapper.Verify(x => x.Map<DomainItem, ServiceItem>(expectedDomainReturn), Times.Once);
}
This test instead of not really checking the functionality of the service.Get(), checks that the parameters passed are correct for the individual dependency calls based on the responses. You are thus not testing AutoMapper itself and should not need to.
Checking result is basically useless but will get the code coverage up.

NSubstitute Async returns null despite defined return object

I have a unit test that should return the specified object but it is returning null.
The Data Provider to test:
public class PlanDataProvider : BaseDomainServiceProvider, IPlanDataProvider
{
//CTOR
public PlanDataProvider(IDataAccessTemplate template, IEntityStore entityStore) : base(template, entityStore)
{
}
public async Task<DefaultActionPlan> GetDefaultActionPlan(string referenceListId)
{
var objectId = GetObjectId(referenceListId);
var defaultActionPlan = await Template.InvokeAsync(context => Task.FromResult(EntityStore.GetEntityById<DefaultActionPlan, ObjectId>
(
context.ActivityContext as IDataAccessContext,
typeof(DefaultActionPlan).FullName,
objectId
)));
}
}
The Test:
public async Task GetPlan_BadPlanID()
{
//Arrange
string badPlanId = "57509afbc6b48d3f33b2dfcd";
...snip...
DefaultActionPlan jj = new ObjectId(badPlanId);
//create EntityStore object
var dataxs = Substitute.For<IDataAccessContext>();
var estore = Substitute.For<IEntityStore>();
estore.GetEntityById<DefaultActionPlan, ObjectId>(
dataxs,
typeof(DefaultActionPlan).FullName,
new ObjectId(badPlanId))
.Returns(Task.FromResult(jj).Result);
var dataAccessTemplate = Substitute.For<IDataAccessTemplate>();
PlanDataProvider pdp = new PlanDataProvider(dataAccessTemplate, estore);
//Act
var t = await pdp.GetDefaultActionPlan(badPlanId);
//Now this confuses me as the compiler thinks t is DefaultActionPlan NOT Task<DefaultActionPlan>???
}
Anyway t returns null eferytime and debugging the test t is null because the GetDefaultActionPlan is not returning jj but instead null??
What am I missing to have jj returned?
Edit:
Both Eris and Gabe rightly pointed out that my Mock of the IEntityStore was not sufficient as that value...even though it specifies a return will not get passed to the wrapping InvokeAsync thus I needed to Mock the InvokeAsync as well.
Gabe's answer was slightly off as passing Arg.Any does not satisfy the InvokeAsync needed parms. However I do not fault him for this as I spent an hours tracking the inheritance chain across multiple projects (this is a big company). Something he does not have access to do.
In the end here is the code that resulted in success:
var estore = Substitute.For<IEntityStore>();
var dataAccessTemplate = Substitute.For<IDataAccessTemplate>();
dataAccessTemplate.InvokeAsync(context => Task.FromResult(
estore.GetEntityById<DefaultActionPlan>(
dataxs, typeof(DefaultActionPlan).FullName, new ObjectId(badPlanId))))
.ReturnsForAnyArgs(jj);
var pdp = new PlanDataProvider(dataAccessTemplate, estore);
While I don't see this in your code, I will assume that GetDefaultActionPlan returns the defaultActionPlan variable, and that Template.InvokeAsync is referring to the IDataAccessTemplate passed in via the constructor.
It looks like you are missing a mock return value for Template.InvokeAsync, and since it is wrapping the other call its return value is the only one you care about:
var estore = Substitute.For<IEntityStore>();
var dataAccessTemplate = Substitute.For<IDataAccessTemplate>();
dataAccessTemplate.InvokeAsync(context => Task.FromResult(Arg.Any<DefaultActionPlan>)
.ReturnsForAnyArgs(jj);
var pdp = new PlanDataProvider(dataAccessTemplate, estore);

DotCover shows 0% for virtual method mock

I'm using DotCover to check the unit test Coverage. Inside one of the method I return results from active directory in SearchResult however, I mocked the class but DotCover displays 0%
public virtual T SearchOneRecord(ISearchConfigurator configurator)
{
var record = (T)Activator.CreateInstance(typeof(T));
var searchResult = configurator.DirectorySearcher.FindOne();
if (searchResult != null)
{
AssignActiveDirectoryValue(searchResult, record);
}
return record;
}
[Test]
public void SearchOneRecord()
{
//Arrange
var configuratorMock = MockRepository.GenerateMock<ISearchConfigurator>();
var searchMock = MockRepository.GenerateStub<Searcher<NativeDs>>();
searchMock.Replay();
var nativeDs = new NativeDs() { PasswordAge = 100 };
searchMock.Expect(x => x.SearchOneRecord(configuratorMock)).Return(nativeDs);
//Act
var record = searchMock.SearchOneRecord(configuratorMock);
//Assert
Assert.AreEqual(nativeDs.PasswordAge, record.PasswordAge);
}
The test passes but since I'm new to RhinoMock (mocking in general) maybe there is an issue .
Any idea?
Correct me if I'm wrong but what you did here is defined mock and then tested if this mock works correctly? I don't think that's something you wanted to achieve. And of course it causes test to pass - mock is working correctly, but it is not testing you application code at all - hence 0% coverage.
What you want to do probably is to mock ISearchConfigurator instance and then pass that mock to method as a parameter.
[Test]
public void SearchOneRecord()
{
//Arrange
var configuratorMock = MockRepository.GenerateMock<ISearchConfigurator>();
var directorySearcherMock = MockRepository.GenerateMock<IDirectorySearcher>(); // please note I don't know exact type, so you need to ammend it
var returnValue = ... // initialize with types you expect DirectorySearcher to return
var searcher = new Searcher(); // initialize class you actually want to test
configurationMock.Replay();
configurationMock.Expect(x => x.DirectorySearcher).Return(directorySearcherMock);
directorySearcher.Expect(x => x.FindOne()).Return(returnValue);
searchMock.Expect(x => x.SearchOneRecord(configuratorMock)).Return(nativeDs);
//Act
var record = searcher.SearchOneRecord(configuratorMock);
//Assert
Assert.AreEqual(nativeDs.PasswordAge, record.PasswordAge);
}
Please note that I'm currently unable to test this code, but it should give you some directions into how it is supposed to be done.

Categories

Resources