Mocking a method return inside tested method - c#

so I am trying to write an unit test for this method (I'm using xUnit and MOQ):
public override FilteredFeedPagesResult<ProgramPage> Create(FilteredFeedContext context)
{
var pages = _pageFilteringService.GetFilteredFeedPages(
context.Reference,
context.Culture,
context.Filters,
_customPageFilter);
if (context.IsSortedByDate && context.PageType.Name is nameof(ProgramPage))
{
var sortedPages = pages.OfType<ProgramPage>()
.Select(page => new
{
Page = page,
ScheduledStartDate = page.GetProgramPairings(page).Select(pairing => pairing.ScheduledStartDate).DefaultIfEmpty(DateTime.MaxValue).Min(),
})
.OrderBy(item => item.ScheduledStartDate)
.ThenBy(item => item.Page.Name)
.Select(item => item.Page);
return Map(sortedPages, context.PageNumber, context.PageSize);
}
return Map(pages.OrderByDescending(x => x.Date), context.PageNumber, context.PageSize);
}
As you can see, inside the LINQ statement in if clause there is a GetProgramPairings being invoked. It is supposed to get events for particular page from the database: Then, based on it, the order of events is created.
Code of the GetProgramPairings method:
public IEnumerable<ProgramPairing> GetProgramPairings(ProgramPage page)
{
var pairings = new List<ProgramPairing>();
if (page != null && page.ProgramPairings != null && page.ProgramPairings.FilteredItems.Any())
{
foreach (ContentAreaItem item in page.ProgramPairings.FilteredItems)
{
if (contentLoader.Service.TryGet<ProgramPairing>(item.ContentLink, out ProgramPairing pairing))
{
pairings.Add(pairing);
}
}
}
return pairings;
}
This is what my test looks like so far:
[Fact]
public void Create_IsSortedByDateTrueAndPageTypeProgramPage_ReturnsSortedPages()
{
var homePageMock = SetupHomePage();
var returnedPages = new[] { CreateProgramPage(DateTime.UtcNow.AddDays(-5)), CreateProgramPage(DateTime.UtcNow) };
var context = new FilteredFeedContext(homePageMock.Object, 0, 6, typeof(ProgramPage), null, null, true);
_filteredFeedPagesFilteringServiceMock.Setup(x => x.GetFilteredFeedPages<ProgramPage>(It.Is<ContentReference>(p => p.ID == homePageMock.Object.ContentLink.ID), It.Is<CultureInfo>(c => c.LCID == homePageMock.Object.Language.LCID), It.IsAny<IDictionary<string, string>>(), It.IsAny<IPageCustomFilter<ProgramPage>>()))
.Returns(returnedPages);
var result = _sut.Create(context);
//will need to create an assert to check if items in the list are in right order
}
My question is, how to mock the IEnumerable parings returned fromGetProgramPairings inside of the main method being tested ?

The problem here is the Single Responsibility Principle. Think thoroughly, is the ProgramPage's responsibility to get program pairings? This logic should be encapsulated in some other service. Once you do that, you can easily mock that service. Good luck!

Related

Remove certain properties from object upon query EF Core 2.1

I'm trying to null or remove the ID entirely of all the queried IsoDataTables before returning them to frontend. The idea is that it should behave (in this case) as a template and I don't want it returning the id's back to me, nor do I want them to be removed in the frontend.
var applicationType = await _context.ApplicationType
.Include(m => m.IsoTableData)
.AsNoTracking()
.FirstOrDefaultAsync(m => m.Id == id);
if (applicationType == null)
{
return NotFound();
}
if (applicationType.IsoTableData != null)
{
foreach (IsoTableData isoTableData in applicationType.IsoTableData)
{
// error since it a not nullable primary key
isoTableData.Id = null;
}
}
return Ok(applicationType);
I have found a workaround in which I duplicate the objects and return them (without saving to DB) but I'm looking for a more elegant solution.
The way I did it was create a copy constructor (or basically, a new instance of an object) with the desired fields; I chose a copy constructor as this logic is recurent in other places as well. Another similar solution is creating a DTO object, but I don't need it here. Any improvements?
//in IsoFileApplicationType.cs
public IsoFileApplicationType(IsoFileApplicationType isoFileApplicationType)
{
Id = null
FullName = isoFileApplicationType.FullName;
Name = isoFileApplicationType.Name;
(...)
foreach (IsoTableData isoTableData in isoFileApplicationType.IsoTableData)
{
IsoTableData.Add(IsoTableData(isoTableData));
}
}
//in IsoTableData.cs
public IsoTableData(IsoTableData isoTableData)
{
Id = null;
Data = isoTableData.Name;
Age = isoTableData.Age;
(...)
}
// in CRUD controller
var applicationType = await _context.ApplicationType
.Include(m => m.IsoTableData)
.AsNoTracking()
.FirstOrDefaultAsync(m => m.Id == id);
if (applicationType == null)
{
return NotFound();
}
IsoFileApplicationType newIsoFileApplicationType = IsoFileApplicationType(applicationType);
return Ok(newIsoFileApplicationType);

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

Conditionally Include and ThenInclude

As commonly known in EF-Core there is no Lazy loading. So that kind of means I'm forced to do my queries with some afterthought. So since I have to think, then i might as well try to do it properly.
I have a Fairly standard update query, but I thought hey, I don't always have to include the HeaderImage and PromoImage FK-objects. There should be a way to make that happen. But I can just not find a way to perform a Include at a later point. In-fact I would like to maybe include right before I actually do work on the object. That way i might be able to automate some of the eagerness.
ArticleContent ac = _ctx.ArticleContents
.Include(a=> a.Metadata)
.Include(a=> a.HeaderImage)
.Include(a=> a.PromoImage)
.Single(a => a.Id == model.BaseID);
ac.Title = model.Title;
ac.Ingress = model.Ingress;
ac.Body = model.Body;
ac.Footer = model.Footer;
if (model.HeaderImage != null)
{
ac.HeaderImage.FileURL = await StoreImage(model.HeaderImage, $"Header_{model.Title.Replace(" ", "_")}_{rand.Next()}");
}
if (model.PromoImage != null)
{
ac.PromoImage.FileURL = await StoreImage(model.PromoImage, $"Promo_{model.Title.Replace(" ", "_")}_{rand.Next()}");
}
ac.Metadata.EditedById = uId;
ac.Metadata.LastChangedTimestamp = DateTime.Now;
await _ctx.SaveChangesAsync();
EXTRA
To be clear, this is EF7 (Core), and im after a solution that allows me to add includes on demand, hopefully after the initial _ctx.ArticleContents.Include(a=> a.Metadata).Single(a => a.Id == model.BaseID).
I'm using something similar to Alexander Derck's solution. (Regarding the exception mentioned in the comments: ctx.ArticleContents.AsQueryable() should also work.)
For a couple of CRUD MVC sites I'm using a BaseAdminController. In the derived concrete Controllers I can add Includes dynamically. From the BaseAdminController:
// TModel: e.g. ArticleContent
private List<Expression<Func<TModel, object>>> includeIndexExpressionList = new List<Expression<Func<TModel, object>>>();
protected void AddIncludes(Expression<Func<TModel, object>> includeExpression)
{
includeIndexExpressionList.Add(includeExpression);
}
Later I saw that I need more flexibility, so I added a queryable. E.g. for ThenInclude().
private Func<IQueryable<TModel>, IQueryable<TModel>> IndexAdditionalQuery { get; set; }
protected void SetAdditionalQuery(Func<IQueryable<TModel>, IQueryable<TModel>> queryable)
{
IndexAdditionalQuery = queryable;
}
Here the Index action:
public virtual async Task<IActionResult> Index()
{
// dynamic include:
// dbset is for instance ctx.ArticleContents
var queryable = includeIndexExpressionList
.Aggregate(dbSet.AsQueryable(), (current, include) => current.Include(include));
if(IndexAdditionalQuery != null) queryable = IndexAdditionalQuery(queryable);
var list = await queryable.Take(100).AsNoTracking().ToListAsync();
var viewModelList = list.Map<IList<TModel>, IList<TViewModel>>();
return View(viewModelList);
}
In the concrete Controller I use:
AddIncludes(e => e.EventCategory);
SetAdditionalQuery(q => q
.Include(e => e.Event2Locations)
.ThenInclude(j => j.Location));
I would create a method to fetch the data which takes the properties to include as expressions:
static ArticleContent GetArticleContent(int ID,
params Expression<Func<ArticleContent, object>>[] includes)
{
using(var ctx = new MyContext())
{
var acQuery = _ctx.ArticleContents.Include(a=> a.Metadata);
foreach(var include in includes)
acQuery = acQuery.Include(include);
return acQuery.Single(a => a.Id == model.BaseID);
}
}
And then in main method:
var ac = GetArticleContent(3, a => a.HeaderImage, a => a.PromoImage);

Moq Verify that Method was called with any expression

I'm new to Moq and I'm trying to Mock my repository.
The method I'm Writing a unit test for is calling the repository like this:
var paymentState = _agreementPaymentStateRepository.SingleOrDefault(
s => s.Agreement.ID == agreementID);
I'm trying to set up my moq like this:
_agreementPaymentStateRepositoryMock
.Setup(m => m.SingleOrDefault(s => s.AgreementID == 1))
.Returns(AgreementPayMentStateMocks.GetOne);
I pass my mocked repository to the class but the paymentState variable is null after the call is made. (I would gladely skip specifying the expression as well).
Any help is much appreciated.
public PaymentState GetPaymentState(int agreementID)
{
try
{
_log.AgreementPaymentStateServiceGetStateStart(agreementID);
var paymentState =
_agreementPaymentStateRepository.SingleOrDefault(s => s.Agreement.ID == agreementID);
var stateToGet = MapStateToGet(paymentState);
_log.AgreementPaymentStateServiceGetStateReturn(agreementID, paymentState.LatestStatus);
return stateToGet;
}
catch (Exception ex)
{
_log.ServiceException(ex.ToString());
throw;
}
}
and the test:
var paymentState = AgreementPayMentStateMocks.GetPayMentState();
_agreementPaymentStateRepositoryMock.Setup(m => m.SingleOrDefault(s => s.AgreementID == 1)).Returns(AgreementPayMentStateMocks.GetOne);
var service = new AgreementPaymentStateService(_agreementPaymentStateRepositoryMock.Object, _log.Object);
var result = service.GetPaymentState(1);
_agreementPaymentStateRepositoryMock.Verify(m => m.Match(aps => aps.SingleOrDefault(s => s.AgreementID == 1)), Times.Exactly(1));
Instead of having a concrete predicate in the SingleOrDefault call, use Moq's It.IsAny<T> method:
_agreementPaymentStateRepositoryMock
.Setup(m => m.SingleOrDefault(It.IsAny<Expression<Func<bool,PaymentState>>>()))
.Returns(AgreementPayMentStateMocks.GetOne);

Reuse query in many object context

I was working this weekend on parallelizing a section of code using Tasks to run all the queries I needed for a dashboard page.
What I have now is many copy/paste methods with almost exactly the same query and a different line at the very end of the method.
Is there a way to write a query against one object context then detach it and pass to a method?
I want to do something like this:
using(DbContext db = new DbContext)
{
var query = db.cars.where(x => x.make == "Ford");
int handleCounts = getDoorHandleCounts(query);
}
public int getDoorHandleCounts(type? query)
{
using(DbContext db = new DbContext())
{
return query.where(x => x.partType == "DoorHandle").Count();
}
}
Any ideas?
Keep in mind all my count() methods are launched from a Task array so they'll be running in parallel. I need a new object context to run each count query on.
I've done some googling and thought about trying to use a pre-compiled query and call it from different object context's, but my real query is kind of complex with allot of if blocks to determine the where condition. Can you compile a query that isn't really simple?
You can't detach and attach a query from/to a context. However, you could reuse the first expression:
Expression<Func<Car,bool>> InitialSelection()
{
return x => x.make == "Ford";
}
public int GetDoorHandleCounts()
{
using(DbContext db = new DbContext())
{
return db.Cars()
.Where(InitialSelection())
.Where(x => x.partType == "DoorHandle").Count();
}
}
And in your task:
int handleCounts = getDoorHandleCounts();
This only works if the initial query is "simple", i.e. does not contain joins and predicates on joined sets that you should repeat over and over in each getX method.
As an alternative, you could initialize a context and feed it to a method that returns a query:
public IQueryable<Car> GetInitialQuery(DbContext db)
{
return db.Cars().Join(....)
.Where(x => x.make == "Ford")
.Where(....);
}
public int GetDoorHandleCounts()
{
using(DbContext db = new DbContext())
{
return GetInitialQuery(db)
.Where(x => x.partType == "DoorHandle").Count();
}
}
Maybe I'm misunderstanding the question, but wouldn't this do what you're looking for?
using(DbContext db = new DbContext)
{
var carsResult = db.cars.where(x => x.make == "Ford");
int handleCounts = getDoorHandleCounts(carsResult);
}
public int getDoorHandleCounts(IEnumerable<Car> result)
{
return result.where(x => x.partType == "DoorHandle").Count();
}
Edit: never mind, I only now saw your mention of the Task array.
Change
public int getDoorHandleCounts(type? query)
to
public int getDoorHandleCounts(IQueryable<cars> query)
And replace cars with the of objects the query will return.
Edit
I would just recommend passing value you're looking to filter on, like this:
{
...
int handleCounts = getDoorHandleCounts("Ford");
}
public int getDoorHandleCounts(string Make)
{
using(DbContext db = new DbContext())
{
return db.cars.where(x => x.make == Make && x.partType == "DoorHandle").Count();
}
}

Categories