Mocking an attribute change on a parameter - using Moq - c#

I am using Moq to mock my Repository layer so I can unit test.
My repository layer Insert methods update the Id property of my entities when a successful db insert occurs.
How do I configure moq to update the Id property of the entity when the Insert method is called?
Repository code:-
void IAccountRepository.InsertAccount(AccountEntity account);
Unit Test:-
[TestInitialize()]
public void MyTestInitialize()
{
accountRepository = new Mock<IAccountRepository>();
contactRepository = new Mock<IContactRepository>();
contractRepository = new Mock<IContractRepository>();
planRepository = new Mock<IPlanRepository>();
generator = new Mock<NumberGenerator>();
service = new ContractService(contractRepository.Object, accountRepository.Object,
planRepository.Object, contactRepository.Object, generator.Object);
}
[TestMethod]
public void SubmitNewContractTest()
{
// Setup Mock Objects
planRepository
.Expect(p => p.GetPlan(1))
.Returns(new PlanEntity() { Id = 1 });
generator
.Expect(p => p.GenerateAccountNumber())
.Returns("AC0001");
// Not sure what to do here?
// How to mock updating the Id field for Inserts?
//
// Creates a correctly populated NewContractRequest instance
NewContractRequest request = CreateNewContractRequestFullyPopulated();
NewContractResponse response = service.SubmitNewContract(request);
Assert.IsTrue(response.IsSuccessful);
}
implementation snippet from ContractService class (WCF service contract).
AccountEntity account = new AccountEntity()
{
AccountName = request.Contact.Name,
AccountNumber = accountNumber,
BillingMethod = BillingMethod.CreditCard,
IsInvoiceRoot = true,
BillingAddressType = BillingAddressType.Postal,
ContactId = request.Contact.Id.Value
};
accountRepository.InsertAccount(account);
if (account.Id == null)
{
// ERROR
}
I apologise if this information may be a little lacking. I only started learning moq and mocking frameworks today. ac

You can use the Callback method to mock side-effects. Something like:
accountRepository
.Expect(r => r.InsertAccount(account))
.Callback(() => account.ID = 1);
That's untested but it's along the right lines.

I'm not sure how that will work because account is created inside the method, so it's not the instance I'll be referring to when I set the value of ID to 1.
Perhaps there's a flaw in my design and I should be checking for ID > 0 inside the IAccountRepository.InsertAccount implementation and then returning a bool if it's ok. Though then I've a problem with inserting an account that has a related object that needs to be insterted (and an Id genereated).
I found this to be the answer to my problem
accountRepository
.Expect(p => p.InsertAccount(It.Is<AccountEntity>(x => x.Id == null)))
.Callback<AccountEntity>(a => a.Id = 1);
thanks.

Related

How to prevent automatic inclusion of navigation properties in unit tests

I am currently developing an app store style API which has the following entities (plus many others, but not relevant to the problem):
App (1 to many relationship to AppRevision - contains IEnumerable property)
AppRevision
Installation
I have come across an odd problem where the behaviour of EF differs in unit tests to when actually running the API, in that navigation properties are automatically being included when unit testing.
Take the following code snippet from my command handler:
App app = await this.context.Apps
.Include(a => a.Installations)
.FirstOrDefaultAsync(a => a.Id == command.AppId);
if (app != null) {
// Code omitted for brevity
}
When running the API, if I inspect app after this code has been run, the AppRevisions collection on the App entity is empty, as you would expect as I have not expliclity told EF to .Include(a => a.AppRevisions) - the API then throws an exception when trying to process code later on that needs this data to be there.
Now look at the following unit test for the same handler:
[Fact]
public async void Handle_ShouldAddInstallationRecord_WhenDataIsValid()
{
Guid testGuid = Guid.NewGuid();
CreateInstallationCommand command = new CreateInstallationCommand(testGuid, "ABC", "abc#abc.com", null);
using (TestContext context = new TestContextFactory().CreateTestContext())
{
context.Apps.Add(new App() { Id = testGuid });
context.AppRevisions.Add(new AppRevision() { Id = Guid.NewGuid(), AppId = testGuid, Status = AppRevisionStatus.Approved, IsListed = true });
await context.SaveChangesAsync();
CreateInstallationCommandHandler handler = new CreateInstallationCommandHandler(context);
CommandResult result = await handler.Handle(command, new CancellationToken());
Assert.True(result);
Assert.Single(context.Installations);
}
}
If I step through this test, when I get to the handler and inspect the app variable, the AppRevisions collection has automatically been populated. As a result, the test passes because the code that requires the AppRevisions collection to be populated can execute.
The expectation is that this test should actually fail, because I'm not telling EF to include those entities in the query.
I am using a Sqlite in memory database to create the database context for my unit tests and running .NET Core 2.2
I originally thought this was something to do with the changetracker. While disabling this does solve the immediate problem reported above, it creates a load of other problems so isn't a viable solution (and probably wouldn't be the correct one anyway)
Any suggestions gratefully received
For anyone who comes across this post in the future, the solution is as per the comments on the original question, to use separate contexts for seeding test data and getting the data later in the test:
[Fact]
public async void Handle_ShouldAddInstallationRecord_WhenDataIsValid()
{
Guid testGuid = Guid.NewGuid();
CreateInstallationCommand command = new CreateInstallationCommand(testGuid, "ABC", "abc#abc.com", null);
using (TestContextFactory contextFactory = new TestContextFactory())
{
using (TestContext seedContext = contextFactory.CreateTestContext())
{
seedContext.Apps.Add(new App() { Id = testGuid });
seedContext.AppRevisions.Add(new AppRevision() { Id = Guid.NewGuid(), AppId = testGuid, Status = AppRevisionStatus.Approved, IsListed = true });
await seedContext.SaveChangesAsync();
}
using (TestContext getContext = contextFactory.CreateTestContext())
{
CreateInstallationCommandHandler handler = new CreateInstallationCommandHandler(getContext);
CommandResult result = await handler.Handle(command, new CancellationToken());
Assert.True(result);
Assert.Single(getContext.Installations);
}
}
}

XUnit how to mock IMemoryCache ASP.NET Core

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.

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.

Cached values returned after TransactionScope.Dispose()

I'm a newbie in integration tests and I'm looking for some explanation and advice about workaround of my issue:
I'm using TransactionScope in my tests to keep database clean and creating new TransactionScope before each test and dispose it after each test:
[SetUp]
public void Init()
{
this.scope = new TransactionScope(
TransactionScopeOption.Required,
new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted },
TransactionScopeAsyncFlowOption.Enabled);
this.context = new SportsPerformanceDbContext();
this.questRepo = new QuestionRepository(this.context);
}
[TearDown]
public void CleanAll()
{
this.context.Dispose();
this.scope.Dispose();
}
Everything works ok when I run one test class. But when I run at least two test classes, I'm facing a problem: in this test (see below) lasrQuestionId equals the last question id from database - that's ok, but actualResultId equals the Id_of_the_last_added_question_in_tests_with_transaction_scope + 1:
[Test]
public async void AddAsyncTest()
{
// Arrange
var questionModel = new QuestionModel
{
//some properties
};
Question lastQuestion = this.GetLastQuestion();
var lastQuestionId = lastQuestion?.Id ?? 0;
// Act
var addResult = await this.questRepo.AddAsync(questionModel);
var actualResult = addResult.Value;
// Assert
Assert.AreEqual(lastQuestionId + 1, actualResult.Id);
// some other assertions
}
So I have the following, e.g., lastQuestionId is 5 (5 questions in Database), but actualResult Id is 16 (because I've previously added some questions in other tests)... I assume that there is a problem with my context or scope.dispose(). I don't know where is a problem, could you explain what am I doing wrong here? Thanks in advance!
this.GetLastQuestion() code is below:
private Question GetLastQuestion()
{
using (var ctx = new SportsPerformanceDbContext())
{
return ctx.Question
.OrderByDescending(q => q.Id)
.FirstOrDefault();
}
}
This is how SQL Server engine works:
For a given identity property with specific seed/increment, the identity values are not reused by the engine. If a particular insert statement fails or if the insert statement is rolled back then the consumed identity values are lost and will not be generated again. This can result in gaps when the subsequent identity values are generated.

Categories

Resources