Not expected result when use Linq Select - c#

I implement unit tests for the engine and setup 2 different methods in the repository mock. So the first one works well, but when I do linq Select for the second one it returns 0, whereas I did setup to return specific object.
My code in Engine:
private readonly IEmployerWorkersClient _employerWorkersClient;
private readonly IJobsClient _jobsClient;
public EmployerWorkerEngine(IEmployerWorkersClient employerWorkersClient, IJobsClient jobsClient,)
{
_employerWorkersClient = employerWorkersClient;
_jobsClient = jobsClient;
}
public async Task<Grid<WorkerFiltered>> GetWorkersAsync(int employerId, GridState gridState)
{
var employerWorkers = await _employerWorkersClient.GetEmployerWorkersByEmployerIdAsync(employerId);
int? payrollId = null;
int? jobRoleId = null;
DateTime? bookedStart = null;
// !!!the following result is Empty collection!!!
List<JobRoleExtended> jobRoles = (await _jobsClient.GetJobRoleExtendedByEmployerWorkerIdsAsync(employerWorkers.Select(ew => ew.Id), payrollId, jobRoleId, bookedStart)).ToList();
// Other things
}
And hereafter my unit test class
private readonly EmployerWorkerEngine _employerWorkerEngine;
private readonly Mock<IEmployerWorkersClient> _employerWorkersClientMock;
private readonly Mock<IJobsClient> _jobClientMock;
public WorkersFilterTests()
{
_employerWorkersClientMock = new Mock<IEmployerWorkersClient>();
_jobClientMock = new Mock<IJobsClient>();
_employerWorkerEngine = new EmployerWorkerEngine(_employerWorkersClientMock.Object, _jobClientMock.Object,);
}
[Theory]
[InlineData(1)]
public async Task GetFilteredWorkersByEmployerIdSuccessSimple(int employerId)
{
// Arrange
const int employerWorkerId = 3;
var gridState = new GridState { Skip = 0, Take = 1 };
var employerWorkers = new List<EmployerWorker> { new EmployerWorker {EmployerId = 1, WorkerId = 2, Id = employerWorkerId} };
_employerWorkersClientMock.Setup(client => client.GetEmployerWorkersByEmployerIdAsync(employerId))
.ReturnsAsync(() => employerWorkers);
var jobRolesExtended = new List<JobRoleExtended>
{
new JobRoleExtended
{
EmployerWorkerId = employerWorkerId,
BookedStartDate = DateTime.UtcNow,
BookedEndDate = DateTime.UtcNow.AddDays(1),
Id = 5,
JobId = 8,
Name = "Job 5",
PayrollId = 10,
PayrollName = "Conduct"
}
};
_jobClientMock
.Setup(client => client.GetJobRoleExtendedByEmployerWorkerIdsAsync(employerWorkers.Select(ew => ew.Id), null, null, null))
.ReturnsAsync(() => jobRolesExtended);
}
So, during testing the following method returns Empty collection:
_jobsClient.GetJobRoleExtendedByEmployerWorkerIdsAsync() and I don't get why.
I assume that it related with the reference of the collection that generates linq Select, but even in this case, I don't know how to handle it for testing.
Can somebody help me with that?

does this help:
_jobClientMock.Setup(client => client.GetJobRoleExtendedByEmployerWorkerIdsAsync(It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>())
.ReturnsAsync(() => jobRolesExtended);
(do check if I got the types right)
if this is the case, then your missing something with your parameters.

With Moq you have to setup the expectations correctly or else when called the mock wont match and thus not perform as expected.
In the method under test you have
int? payrollId = null;
int? jobRoleId = null;
DateTime? bookedStart = null;
List<JobRoleExtended> jobRoles =
(await _jobsClient.GetJobRoleExtendedByEmployerWorkerIdsAsync(
employerWorkers.Select(ew => ew.Id), payrollId, jobRoleId, bookedStart)
)
.ToList();
The Select statement appears to be returning a collection of Ids (IEnumerable<int>) and you have already configured the first call correctly.
You now need to correctly configure the second call to expect that collection of int Ids
_jobClientMock
.Setup(_ => _.GetJobRoleExtendedByEmployerWorkerIdsAsync(
It.IsAny<IEnumerable<int>>(),
null,
null,
null)
)
.ReturnsAsync(() => jobRolesExtended);

Related

Why XUnit Test case throwing "Moq.MockException : invocation failed" error, where my inputs all are correct?

while interface mocking I am trying to send with actual inputs in model object, but getting failure with
Moq.MockException : ISearchRepo.GetEsDataWithBoolByAggregation<IndexVM>(BoolMustMatchAggregationIM) invocation failed with mock behavior Strict.
All invocations on the mock must have a corresponding setup.
error message.
Mock method for interface input object (which is sending same like in manual)
private BoolMustMatchAggregationIM GetBoolMustMatchAggregationIMMockData()
{
var objInputs = GetTopologyTabularApplyFilterMockData();
var studentFieldValue = objInputs.StudentId != null ? string.Join(',', objInputs.StudentId) : string.Empty;
BoolMustMatchAggregationIM topoplogyApplyFilter = new BoolMustMatchAggregationIM()
{
From = objInputs.From,
Size = objInputs.Size,
IndexName = ElasticSearchIndexConstant.Index,
FirstField = DataConstant.MarketField,
FirstFieldValue = objInputs.Market,
SecondField = DataConstant.StudentField,
SecondFieldValue = studentFieldValue,
ThirdField = DataConstant.SubjectField,
ThirdFieldValue = objInputs.SubjectId != null ? objInputs.SubjectId : null,
FourthField = DataConstant.SiteIdFieldName,
FourthFieldValue = objInputs.SiteId != null ? objInputs.SiteId[0] : null,
OperatorType = DataConstant.NestAndOperator,
CardinalField = DataConstant.CardinalField,
SumName = DataConstant.SumName,
MinimumMatch = DataConstant.One
};
return topoplogyApplyFilter;
}
Mock method for output object from interface
private IEnumerable<SiteSubjectIndexVM> GetSiteSubjectIndexClassMockData()
{
List<SiteSubjectDetails> objListSiteSubject = new List<SiteSubjectDetails>();
SiteSubjectDetails objSiteSubject = new SiteSubjectDetails { SubjectId = 4002453 };
objListSiteSubject.Add(objSiteSubject);
List<SiteSubjectIndexVM> objListSiteSubjectIndexClass = new List<SiteSubjectIndexVM>();
SiteSubjectIndexVM objSiteSubjectIndexClass = new SiteSubjectIndexVM()
{
Id = 123,
StudentId = "SE123",
Longitude = -122.51m,
Latitude = 47.66m,
Region = "Selected",
SiteId = "SE03D123",
HasSubjectSites = 1,
Market = "HNK",
TimeStamp = new DateTime(2022, 08, 07, 11, 02, 51, 167),
AAVStatus = "Selected"
};
objListSiteSubjectIndexClass.Add(objSiteSubjectIndexClass);
objSiteSubjectIndexClass = new SiteSubjectIndexVM()
{
Id = 456,
SiteId = "SE04D456",
Subjects = objListSiteSubject,
StudentId = "SE456",
Latitude = 47.74m,
Longitude = -122.15m,
Market = "WGL",
TimeStamp = new DateTime(2022, 08, 07, 11, 02, 51, 167),
Region = "WEST",
HasSubjectSites = 1
};
objListSiteSubjectIndexClass.Add(objSiteSubjectIndexClass);
return objListSiteSubjectIndexClass;
}
Test Case
[Fact]
public async Task GetTabularApplyFilterAsync_WhenMarketIsNotNull_ReturnsSiteSubjectsIndexFromES()
{
//Arrange
var objRepoIM = GetBoolMustMatchAggregationIMMockData();
var objRepoVM = GetSiteSubjectIndexClassMockData().Where(x=>x.Id== 72337696).ToList();
var objIMapperVM = GetSiteSubjectIndexMockData().Where(x => x.Id == 72337696).ToList();
var objServiceIM = GetTabularApplyFilterMockData();
objServiceIM.StudentId = null; objServiceIM.SubjectId = null; objServiceIM.SiteId = null;
//Action
_mockISearchRepo.Setup(x => x.GetEsDataWithBoolByAggregationAsync<SiteSubjectIndexVM>(objRepoIM)).ReturnsAsync(objRepoVM);
_mockMapper.Setup(x => x.Map<IEnumerable<SiteSubjectIndex>>(objRepoVM)).Returns(objIMapperVM);
var subjectService = this.CreateSearchService();
var result = await subjectService.GetTabularApplyFilterAsync(objServiceIM).ConfigureAwait(false);
//Assert
Assert.NotNull(result);
Assert.Equal(result, objIMapperVM);
_mockRepository.VerifyAll();
}
by _mockIElasticSearchRepo.Setup(x => x.GetEsDataWithBoolByAggregationAsync<SiteLinkIndexVM>(It.IsAny<BoolMustMatchAggregationIM>())).ReturnsAsync(objRepoVM);
error get resolved, but why? where I have given correct input model with correct values.
The error message signals that there is no setup for the call. The following setup will be met, if the parameter is equal to the object that you created:
_mockISearchRepo.Setup(x => x.GetEsDataWithBoolByAggregationAsync<SiteSubjectIndexVM>(objRepoIM)).ReturnsAsync(objRepoVM);
Most likely, the call to GetEsDataWithBoolByAggregationAsync in the code under test does not use the exact same instance that you created in your test, but creates an instance of its own. Hence check will fail because it is not the same object and the setup is not matched.
You can work around this by defining the setup so that you check the parameters dynamically, e.g.
_mockISearchRepo
.Setup(x => x.GetEsDataWithBoolByAggregationAsync<SiteSubjectIndexVM>(It.Is<BoolMustMatchAggregationIM>(y => y.IndexName == ElasticSearchIndexConstant.Index))
.ReturnsAsync(objRepoVM);
In above sample, I have only included a very simple check for the index name. You can extend the expression to check the input more thoroughly.
By using this approach, the setup will be matched as soon as the expression returns true so this also works if the code under test creates the instance that is used as the parameter.

how can I reduce 2 select to 1 select linq to gain performance?

my question is simple but I got stuck with something. Can you tell me how can I reduce 2 select into 1 select LINQ in c#? I am using CloudNative.CloudEvents NuGet package for cloud-native events.
var orderEvents = input
.Select(_ => new OrderDocument(_.Id, _.ToString()).ToOrderEvent())
.Select(_ =>
new CloudEvent()
{
Type = _.EventType,
Subject = _.Subject,
Source = _.Source,
Data = _
});
input is a parameter from cosmosDbTrigger it`s type : IReadOnlyList
OrderDocument.cs
public class OrderDocument
{
public string Id { get; private set; }
public string Json { get; private set; }
public OrderDocument(string id, string json)
{
Id = id;
Json = json;
}
public OrderEvent ToOrderEvent() => OrderEventHelper.ToOrderEvent(Json);
}
OrderEventHelper.cs
public static OrderEvent ToOrderEvent(string json)
{
ArgumentHelper.ThrowIfNullOrEmpty(json);
var orderEvent = JsonConvert.DeserializeObject<OrderEvent>(json);
var eventDefinition = OrderEvents.EventDefinitions.SingleOrDefault(_ => _.EventType == orderEvent.EventType);
return eventDefinition == null
? orderEvent
: new OrderEvent(
orderEvent.Id,
orderEvent.Source,
orderEvent.EventType,
orderEvent.Subject,
orderEvent.DataContentType,
orderEvent.DataSchema,
orderEvent.Timestamp,
JsonConvert.DeserializeObject(orderEvent.Payload.ToString(), eventDefinition.PayloadType),
orderEvent.TraceId);
}
linq extensions are basically for loops in the background. If you want to perform multiple actions against a list, perhaps making your own simple for loop where you can manage that yourself would work.
Your code:
var orderEvents = input
.Select(_ => new OrderDocument(_.Id, _.ToString()).ToOrderEvent())
.Select(_ =>
new CloudEvent()
{
Type = _.EventType,
Subject = _.Subject,
Source = _.Source,
Data = _
});
could be changed to:
// our result set, rather than the one returned from linq Select
var results = new List<CloudEvent>();
foreach(var x in input){
// create the order event here
var temporaryOrderEvent = new OrderDocument(x.Id, x.ToString()).ToOrderEvent();
// add the Cloud event to our result set
results.Add(new CloudEvent()
{
Type = temporaryOrderEvent .EventType,
Subject = temporaryOrderEvent .Subject,
Source = temporaryOrderEvent .Source,
Data = temporaryOrderEvent
});
}
where you then have a result list to work with.
If you wanted to keep it all in linq, you could instead perform all of your logic in the first Select, and ensure that it returns a CloudEvent. Notice here that you can employ the use of curly brackets in the linq statement to evaluate a function rather than a single variable value:
var orderEvents = input
.Select(x =>
{
// create the order event here
var temporaryOrderEvent = new OrderDocument(x.Id, x.ToString()).ToOrderEvent();
// return the Cloud event here
return new CloudEvent()
{
Type = temporaryOrderEvent .EventType,
Subject = temporaryOrderEvent .Subject,
Source = temporaryOrderEvent .Source,
Data = temporaryOrderEvent
};
});
How about putting conversion to OrderEvent and using ToCloudEvent in the same Select?
var orderEvents = input
.Select(_ => new OrderDocument(_.Id, _.ToString()).ToOrderEvent().ToCloudEvent())
public class OrderEvent
{
public CloudEvent ToCloudEvent()
{
new CloudEvent()
{
Type = this.EventType,
Subject = this.Subject,
Source = this.Source,
Data = this
};
}
}

How to use Projection in LINQ to convert entity to DTO class

I have Linq script and I want to use projection class to get data to DTO type. I got the example for lambda expersion but getting error on the LINQ script.
Linq Script:
public class EziTransactionDto
{
... other properties
public static Expression<Func<EziTransactionEntity, EziTransactionDto>> Projection()
{
return eziTransactionDto => new EziTransactionDto
{
EziTransactionId = eziTransactionDto.Id,
LoginSiteID = eziTransactionDto.LoginSiteID,
WorkCodes = eziTransactionDto.WorkCodes
};
}
Linq query:
var ts = (from transaction in _eziTransactionRepository.GetAll<EziTransactionEntity>()
where transaction.LoginErrorCode != 0
select transaction
).Select(EziTransactionDto.Projection);
Error:
I guess the keyword Expression is odd there.
Try this:
// public static Expression<Func<EziTransactionEntity, EziTransactionDto>> Projection()
public static Func<EziTransactionEntity, EziTransactionDto> Projection()
{
return eziTransactionDto => new EziTransactionDto
{
EziTransactionId = eziTransactionDto.Id,
LoginSiteID = eziTransactionDto.LoginSiteID,
WorkCodes = eziTransactionDto.WorkCodes
};
}
After the first Select your IQueryable has already fetched the data to the local process, and made it an IEnumerable.
You could do this conversion in your Select statement:
var eziTransactionDtos = _eziTransactionRepository.EziTransactionEntities
.Where(eziTransactionEntity => eziTransationEntity.LoginErrorCode != 0)
.Select(eziTransactionEntity => new EziTransactionDto
{
EziTransactionId = eziTransactionDto.Id,
LoginSiteID = eziTransactionDto.LoginSiteID,
WorkCodes = eziTransactionDto.WorkCodes,
});
However, if you need to convert EziTransactionEntities to EziTransactionDtos on several places, it is a good idea to create a reusable extension method for IQueryable<EziTransactionEntities>.
If you are not familiar with extension methods, see extension methods demystified
public static IQueryable<EziTransactionDto> ToEziTransactionDto(
this IQueryable<EziTransactionEntity> eziTransactionEntities)
{
return eziTransactionEntities.Select(eziTransactionEntity => new EziTransactionDto
{
EziTransactionId = eziTransactionDto.Id,
LoginSiteID = eziTransactionDto.LoginSiteID,
WorkCodes = eziTransactionDto.WorkCodes,
});
Usage:
var eziTransactionDtos = eziTransactionRepository.EziTransactionEntities
.Where(eziTransactionEntity => eziTransationEntity.LoginErrorCode != 0)
.ToEziTransactionDtos();
Reusable:
var transactionWithoutWorkCodes = eziTransactionRepository.EziTransactionEntities
.Where(eziTransactionEntity => eziTransationEntity.WorkCode == null)
.ToEziTransactionDtos();
Easy to unit test:
List<EziTransactionEntity> testItems = ...
List<EziTransactionDto> expectedResults = ...
var testResults = testItems.AsQueryable().ToEziTransactionDtos();
Assert.AreQual(expectedResults, testResults, unorderedTransactionsComparer);
Easy to maintain: if you add / remove / change one property of this conversion, you'll only have to do this on one location
fond the answer
public static Expression<Func<EziTransactionEntity, EziTransactionDto>> Projection
{
get
{
return eziTransactionDto => new EziTransactionDto
{
EziTransactionId = eziTransactionDto.Id,
LoginSiteID = eziTransactionDto.LoginSiteID,
WorkCodes = eziTransactionDto.WorkCodes
};
}
}
..
var ts = (from transaction in _eziTransactionRepository.GetAll<EziTransactionEntity>()
select transaction
).Select(EziTransactionDto.Projection).ToList();

Unit Testing xUnit Asserting 2 Collections with Moq Interface

Good day everyone,
I'm new in xunit and even in unit testing. I have a code here and I'm trying to assert two collection of list. But I have no idea how to assert and pass this test. Here's my code
[Theory]
[InlineData(1)]
public void GetAllStudents_Exempt1(int number)
{
// arrange
var studentRepo = new Mock<IStudentRepository>();
var listOfStudents = new List<Student> { new Student { StudentId = 1, Firstname = "Firstname1", Lastname = "Firstname1" },
new Student{StudentId=2, Firstname="Firstname2",Lastname="Lastname2"} };
var getAllStudentDetailsExempt1 = studentRepo.Setup(s => s.GetStudents()).Returns(listOfStudents.Where(x => x.StudentId != number));
var studentService = new StudentService(studentRepo.Object);
// act
var getStudentsDetails = studentService.ListOfStudentsExempt1(1);
// assert
// I don't have any idea how to assert
}
First a few notes:
var getAllStudentDetailsExempt1 = studentRepo
.Setup(s => s.GetStudents())
.Returns(listOfStudents.Where(x => x.StudentId != number));
you don't need var getAllStudentDetailsExempt1, you can just setup your repo-mock...
studentRepo
.Setup(s => s.GetStudents())
.Returns(listOfStudents.Where(x => x.StudentId != number));
You probably want to change:
// act
var getStudentsDetails = studentService.ListOfStudentsExempt1(1);
to use the number variable...
// act
var getStudentsDetails = studentService.ListOfStudentsExempt1(number);
So then you can assert by checking some properties:
Assert.Equals(1, getStudentsDetails.Count);
Assert.Equals("FirstName1", getStudentsDetails.First().Firstname);
etc. etc.
Give it a shot!

How do you reuse mapping functions on Nested entities in Entity Framework?

I have seen multiple questions that are similar to this one but I think my case is slightly different. I'm using EF6 to query the database and I'm using data projection for better queries.
Given that performance is very important on this project I have to make sure to just read the actual fields that I will use so I have very similar queries that are different for just a few fields as I have done this I have noticed repetition of the code so I'm been thinking on how to reuse code this is currently what I Have:
public static IEnumerable<FundWithReturns> GetSimpleFunds(this DbSet<Fund> funds, IEnumerable<int> fundsId)
{
IQueryable<Fund> query = GetFundsQuery(funds, fundsId);
var results = query
.Select(f => new FundWithReturns
{
Category = f.Category,
ExpenseRatio = f.ExpenseRatio,
FundId = f.FundId,
Name = f.Name,
LatestPrice = f.LatestPrice,
DailyReturns = f.FundDailyReturns
.Where(dr => dr.AdjustedValue != null)
.OrderByDescending(dr => dr.CloseDate)
.Select(dr => new DailyReturnPrice
{
CloseDate = dr.CloseDate,
Value = dr.AdjustedValue.Value,
}),
Returns = f.Returns.Select(r => new ReturnValues
{
Daily = r.AdjDaily,
FiveYear = r.AdjFiveYear,
MTD = r.AdjMTD,
OneYear = r.AdjOneYear,
QTD = r.AdjQTD,
SixMonth = r.AdjSixMonth,
ThreeYear = r.AdjThreeYear,
YTD = r.AdjYTD
}).FirstOrDefault()
})
.ToList();
foreach (var result in results)
{
result.DailyReturns = result.DailyReturns.ConvertClosingPricesToDailyReturns();
}
return results;
}
public static IEnumerable<FundListVm> GetFundListVm(this DbSet<Fund> funds, string type)
{
return funds
.Where(f => f.StatusCode == MetisDataObjectStatusCodes.ACTIVE
&& f.Type == type)
.Select(f => new FundListVm
{
Category = f.Category,
Name = f.Name,
Symbol = f.Symbol,
Yield = f.Yield,
ExpenseRatio = f.ExpenseRatio,
LatestDate = f.LatestDate,
Returns = f.Returns.Select(r => new ReturnValues
{
Daily = r.AdjDaily,
FiveYear = r.AdjFiveYear,
MTD = r.AdjMTD,
OneYear = r.AdjOneYear,
QTD = r.AdjQTD,
SixMonth = r.AdjSixMonth,
ThreeYear = r.AdjThreeYear,
YTD = r.AdjYTD
}).FirstOrDefault()
}).OrderBy(f=>f.Symbol).Take(30).ToList();
}
I'm trying to reuse the part where I map the f.Returns so I tried created a Func<> like the following:
private static Func<Return, ReturnValues> MapToReturnValues = r => new ReturnValues
{
Daily = r.AdjDaily,
FiveYear = r.AdjFiveYear,
MTD = r.AdjMTD,
OneYear = r.AdjOneYear,
QTD = r.AdjQTD,
SixMonth = r.AdjSixMonth,
ThreeYear = r.AdjThreeYear,
YTD = r.AdjYTD
};
and then use like this:
public static IEnumerable<FundListVm> GetFundListVm(this DbSet<Fund> funds, string type)
{
return funds
.Where(f => f.StatusCode == MetisDataObjectStatusCodes.ACTIVE
&& f.Type == type)
.Select(f => new FundListVm
{
Category = f.Category,
Name = f.Name,
Symbol = f.Symbol,
Yield = f.Yield,
ExpenseRatio = f.ExpenseRatio,
LatestDate = f.LatestDate,
Returns = f.Returns.Select(MapToReturnValues).FirstOrDefault()
}).OrderBy(f=>f.Symbol).Take(30).ToList();
}
The compiler is ok with it but at runtime, it crashes and says: Internal .NET Framework Data Provider error 1025
I tried to convert the Func into Expression like I read on some questions and then using compile() but It didn't work using AsEnumerable is also not an option because It will query all the fields first which is what I want to avoid.
Am I trying something not possible?
Thank you for your time.
It definitely needs to be Expression<Func<...>>. But instead of using Compile() method (not supported), you can resolve the compile time error using the AsQueryable() method which is perfectly supported (in EF6, the trick doesn't work in current EF Core).
Given the modified definition
private static Expression<Func<Return, ReturnValues>> MapToReturnValues =
r => new ReturnValues { ... };
the sample usage would be
Returns = f.Returns.AsQueryable().Select(MapToReturnValues).FirstOrDefault()

Categories

Resources