Mock issue - Value cannot be null. (Parameter 'source') - c#

I am trying to test an API service using Moq and am getting the error message:
Value cannot be null. (Parameter 'source').
Below is the code [Which is already working fine in the real world, the Mock is a retrospective project.]
models = new List<MyModel> {
new MyModel
{
Id = 1,
Name = "John Smith",
Status = "Engineer",
Centre = 9999
},
new MyModel
{
Id = 2,
Name = "Jane Doe",
Status = "Manager",
Centre = 9999
} };
var mockDbSet = new Mock<DbSet<MyModel>>();
mockDbSet.As<IQueryable<MyModel>>().Setup(m => m.Provider).Returns(models.AsQueryable().Provider);
mockDbSet.As<IQueryable<MyModel>>().Setup(m => m.Expression).Returns(models.AsQueryable().Expression);
mockDbSet.As<IQueryable<MyModel>>().Setup(m => m.ElementType).Returns(models.AsQueryable().ElementType);
mockDbSet.As<IQueryable<MyModel>>().Setup(m => m.GetEnumerator()).Returns(models.GetEnumerator());
mockDbSet.Setup(m => m.Add(It.IsAny<MyModel>())).Callback<MyModel>(models.Add);
mockContext = new Mock<LiveDbContext>();
mockContext.Setup(m => m.Add(models)).Callback<DbSet<MyModel>>(mockDbSet.Object.AddRange);
myService = new LiveService(mockContext.Object);
var result = myService.GetStaffByCentre(9999).Result.ToList();
//Assert
Assert.AreEqual(1, result.Count);
The line that appears to be causing the error is the
var result = myService.GetStaffByCentre(9999).Result.ToList();

Just to give an update, and perhaps help others in the same situation. I realised that I needed to add a Repository layer and create a mock of it in order to create and run Unit Tests.
The Mock repository used the 'models' list [see original post] as its data source which meant I could remove the need to create a 'Mock Context' object and a mock DbSet.
I found this tutorial very helpful.

We don't really know what your service/SUT is doing because you haven't provided it, but I assume it performs a query on the DbSet<MyModel> and returns the unresolved enumerable as the Result member. If that is the case, the ToList() invocation is where the exception will be occurring, when the enumerable is resolved.
Before we go further, my first question would be can you use the in-memory database provider. Mocking EFCore is hard and Microsoft doesn't recommend it.
If there is a reason you can't use the in-memory database provider, and my assumptions are correct, this is probably occurring because you haven't set up the query provider methods such as CreateQuery and Execute which are used by LINQ extensions. Exactly which you need to mock again I can't say, I don't have enough detail.

Related

Why is my CollectionAssert in my Unit Test always failing in my .NET Application?

I'm writing a unit test which needs to check if two arrays of object match each other. I've tried a few different approaches but all of them fail. The most obvious approach was the CollectionAssert which, according to what I have read, doesn't care about the source only the values and order of the data itself, despite the two array of objects matching each other, the test still fails and I'm lost as to why.
Let's take my unit test
[TestMethod]
public async Task GetMyData_ShouldReturnDataDto_WhenAllIsOk()
{
// Arrange
var userId = "123-456";
Shared.Car customer = new()
{
Id = userId,
OwnerProfile = new Shared.OwnerProfile
{
Title = "Mr",
FirstName = "Bob",
Surname = "Smith",
CarsOwned = new Shared.CarsOwned[]
{
new CarsOwned {
Make = "Ford",
Model = "Mustang",
Rental = false
},
new CarsOwned {
Make = "Ford",
Model = "Fiesta",
Rental = true
}
}
}
};
customerRepo.GetCustomerById(Arg.Any<string>()).Returns(customer);
// Act
var data = await sut.GetCustomerProfile(userId);
// Assert
CollectionAssert.AreEqual(customer.OwnerProfile.CarsOwned, data.CarsOwned);
}
When I set a break point in my test to debug it, I can clearly see that both instances of CarsOwned in the CollectionAssert have identidcal data in the identical order although the sources are different.
Is the CollectionAssert the most appropriate for this setup? I believed it was based on the documentation and the described behaviour.
When I run the test I recieve the error of:
CollectionAssert.AreEqual failed. (Element at index 0 do not match.)
This suggest to me that it's not happy that it's comparing the two arrays from different sources but I could be wrong about that. Any help is appreciated.
I think the issue is that you did not override the Equals() method in the CarsOwned class. I assume that it is this method which is used by CollectionAssert.AreEqual.
By default, the Equals() method return true only if the both parameters have the same reference (~the memory address). It means that in your mind both objects are equals, but for the computer they are completely different objects. So you have to tell to the program how to compare two instance of the same class.
I let you search how implement a correct and efficient Equals() method, but this is not very complicate (because you do not have a lot of field and there is not some complex business logic).
See you, Dylan

How to unit test the expression being passed into the repository layer

I have a service layer which calls a repository layer to fetch data, then do work upon the return object. I want to test the expression being sent into the repository "get" method, say in case the primary key becomes a compound key or something.
Example:
public async Task ArchiveAsync(Domain.Person person)
{
Data.Person entityModel = await _repo.SingleOrDefaultAsync<Data.Person>(p => p.Id == person.Id);
// Stuff is done here...
}
So, in this example, the domain object is passed in, the data object is fetched, and something is done to it. I want to test the expression being passed into the SingleOrDefaultAsync method.
Thoughts?
It's probably better to refactor your code and make the "expression" a proper entity.
The Specification pattern is perfect for your case.
Vladimir Khorikov has written a great,simple article about this: Specification pattern: C# implementation
He has a sample project published on GitHub here
In case the link becomes invalid,I add the main core logic here.
Your repository will look like this:
public IReadOnlyList<T> Find(Specification<T> specification)
{
using (context))
{
return context.DbSet<T>()
.Where(specification.ToExpression())
.ToList();
}
}
For the Specification class you can find many implementations.
It a small utility class that is converted to an expression eventually
If you design your app like that you can easily Test your Specification(which practically is your expression)
I'm not sure that it's a good idea to test expressions. But you may try, if you wish. I have at least one idea for such request.
It will be required to provide a mock instead of your repository, you may use any library you wish. Moq or NSubstitute as an example.
Then, you should get Expression<Func<Domain.Person, bool>> object passed to your repository on SingleOrDefaultAsync invocation. That's should be pretty straightforward with most mocking libs.
And for the last, you will need to compile your expression into Func<Domain.Person, bool> and assert that it returns true for Domain.Person object with expected Id and false otherwise.
And a small snapshot to illustrate, I'm assuming here that there is only one argument, there is the only invocation on repository object and that Domain.Person.Id has public setter.
var repository = Substitute.For<IRepository>();
var service = new Service(repository);
var person = new Domain.Person { Id = 42 };
await service.ArchiveAsync(person);
var call = repository.ReceivedCalls().First();
var arg = call.GetArguments().First();
var expr = (Expression<Func<Domain.Person, bool>>)arg;
var func = expr.Compile();
Assert.True(func(person));
Assert.False(func(new Domain.Person {Id = 1}));

Incorrect setup in MOQ in the Lambda calling a property

MOQ questions often get people pointing back to the documents , which is cool and all but I think I need some help understanding what this error comes from ( maybe my knowledge of lambda expressions is off )
I have a MOQ test
var mockTicket = new Mock<Ticket>();
mockTicket.Setup(tix => tix.Total).Returns(var expectedResult = 5);
cashRegister.PendingTickets.Add(mockTicket.Object);
//act
var actual = cashRegister.CloseTicket(mockTicket.Object);
// FYI . close ticket returns the total of the tickets hence this is logical to the point that the
// ticket is the price being returned.
//assert
Assert.Equals(actual, expectedResult);
and my errors are
System.NotSupportedException: Invalid setup on a non-virtual (overridable in VB) member: tix => tix.Total
and also Total is just a get
public decimal Total
{
get
{
return Decimal.Round(ItemsOrdered.Sum(x => x.Price + x.Tax), 2);
}
}
To do the mocking you would need to have an ITicket interface to pass into your method, then your cashRegister doesn't need a concrete Ticket and you can use a known good value for Total.
Additionally, you don't need the var set in the return. You would be able to use:
var mockTicket = new Mock<ITicket>();
mockTicket.Setup(tix => tix.Total).Returns(5);
Additionally, you would need to change cashRegister.PendingTotal and cashRegister.CloseTicket to have ITicket in the signature instead of Ticket.
Alternatively, you can arrange a concrete Ticket class to return a known good value for Total.
Additionally, I wouldn't alter the Ticket class to have a virtual Total property on it, just to facilitate this testing, you would be allowing the tests to pollute the code needlessly.

Trying to create Moq object

I've got a method which takes a IList<Person> and returns an IEnumberable as so:
internal static IEnumerable<Dictionary<string, string>> GetPersonsPerSite(IList<Person> Data)
{
//Implementation ...
}
and I'm trying to create a mock object of IList<Person> so I can test this method.
Using Moq I have written the following:
var mockObjects = new Mock<IList<Person>>();
mockObjects.Setup(x => x[0]).Returns(new Person()
{
SITE_ID = "test",
MPAN = "test",
ADDLINE1 = "test",
ADDLINE2 = "test",
ADDRESS_LINE_1 = "test",
ADDRESS_LINE_2 = "test"
});
However when I come to use the object the IEnumerable returned is aways throwing exception Object reference not set to an instance of an object.
I'm brand new to Moq, and I'm pretty sure I'm missing a fundamental concept here, however I've successfully being able to throw exceptions and modifiy outputs with other mock objects.
I'd appreciate if someone could point me in the right direction.
Don't mock the IList. You don't need to, unless there's something specific your looking to check.
Instead just keep your test simple and do something like this:
var testData = new List<Person>(){new Person()
{
SITE_ID = "test",
MPAN = "test",
ADDLINE1 = "test",
ADDLINE2 = "test",
ADDRESS_LINE_1 = "test",
ADDRESS_LINE_2 = "test"
}};
var result = GetPersonsePerSite(testData);
The code you have got there looks fine and does work for me. Can you expand with an example of where you actually consume the mockObjects.Object and find it null?
Also, do you really need to mock your IList<Person>?
If you are just using it to supply test data to the method under test, just use a concrete list - there is nothing gained by using a mock for this.
Of course, you may be mocking it so you can verify certain actions are taken on it (methods called, properties accessed, etc) - in which case that is different.

Moq'ing methods where Expression<Func<T, bool>> are passed in as parameters

I'm very new to unit testing and mocking! I'm trying to write some unit tests that covers some code that interacts with a data store. Data access is encapsulated by IRepository:
interface IRepository<T> {
....
IEnumerable<T> FindBy(Expression<Func<T, bool>> predicate);
....
}
The code that I'm trying to test, utilising a concrete IoC'd implementation of IRepository looks like this:
public class SignupLogic {
private Repository<Company> repo = new Repository<Company>();
public void AddNewCompany(Company toAdd) {
Company existingCompany = this.repo.FindBy(c => c.Name == toAdd.Name).FirstOrDefault();
if(existingCompany != null) {
throw new ArgumentException("Company already exists");
}
repo.Add(Company);
repo.Save();
}
}
So that I'm testing the logic of SignupLogic.AddNewCompany() itself, rather than the logic and the concrete Repository, I'm mocking up IRepository and passing it into SignupLogic. The mocked up Repository looks like this:
Mock<Repository> repoMock = new Mock<Repository>();
repoMock.Setup(moq => moq.FindBy(c => c.Name == "Company Inc")....
which returns an in-memory IEnumberable containing a Company object with name set to "Company Inc". The unit test that calls SignupLogic.AddNewCompany sets up a company with duplicate details and trys to pass that in, and I assert that an ArgumentException is thrown with the message "Company already exists". This test isn't passing.
Debugging through the unit test and AddNewCompany() as it runs, it would appear that existingCompany is always null. In desperation, I've found that if I update SignupLogic.AddNewCompany() so that the call to FindBy looks like this:
Company existingCompany = this.repo.FindBy(c => c.Name == "Company Inc").FirstOrDefault();
the test passes, which suggests to me that Moq is only responding to code that is exactly the same as I've setup in my test fixture. Obviously that's not especially useful in testing that any duplicate company is rejected by SignupLogic.AddNewCompany.
I've tried setting up moq.FindBy(...) to use "Is.ItAny", but that doesn't cause the test to pass either.
From everything I'm reading, it would appear that testing Expressions as I'm trying to isn't actually do-able with Moq here. Is it possible? Please help!
It is probably correct that only an Expression with the exactly same structure (and literal values) will match. I suggest that you use the overload of Returns() that lets you use the parameters the mock is called with:
repoMock.Setup(moq => moq.FindBy(It.IsAny<Expression<Func<Company, bool>>>())
.Returns((Expression<Func<Company, bool>> predicate) => ...);
In ..., you can use predicate to return the matching companies (and maybe even throw an exception if the matching companies isn't what you expected). Not very pretty, but I think it will work.
You should be able to use It.IsAny<>() to accomplish what you are looking to do. With the use of It.IsAny<>() you can simply adjust the return type for your setup to test each branch of your code.
It.IsAny<Expression<Func<Company, bool>>>()
First Test, return a company regardless of predicate which will cause the exception to throw:
var repoMock = new Mock<IRepository<Company>>();
repoMock.Setup(moq => moq.FindBy(It.IsAny<Expression<Func<Company, bool>>>())).Returns(new List<Company>{new Company{Name = "Company Inc"}});
var signupLogic = new SignupLogic(repoMock.Object);
signupLogic.AddNewCompany(new Company {Name = "Company Inc"});
//Assert the exception was thrown.
Second Test, make the return type an empty list witch will cause add to be called.:
var repoMock = new Mock<IRepository<Company>>();
repoMock.Setup(moq => moq.FindBy(It.IsAny<Expression<Func<Company, bool>>>())).Returns(new List<Company>());
var signupLogic = new SignupLogic(repoMock.Object);
signupLogic.AddNewCompany(new Company {Name = "Company Inc"});
repoMock.Verify(r => r.Add(It.IsAny<Company>()), Times.Once());
You normally only mock the types you own. Those you do not own, really should not be mocked because of various difficulties. So mocking expressions - as the name of your question implies - is not the way to go.
In Moq framework. It is important to put .Returns() for functions otherwise it is not matched. So if you have not done that, it is your problem.
repoMock.Setup(moq => moq.FindBy(c => c.Name == "Company Inc").Returns(....

Categories