Mock extension methods IElasticClient - c#

Is there a way to mock the following
var result = await Client.SearchAsync<IndexedSite>(d => d
.Index(SiteIndexName)
.Query(q => q.MatchAll())
.Sort(sd => sd.Field(s => s.Name, SortOrder.Ascending))
.Take(c_maxSiteListSize));
Would one use .Callbacks in this situation?
My current setup:
private Mock<IElasticClient> _client = new Mock<IElasticClient>();
private Mock<ISearchResponse<IndexedSite>> indexedSite = new Mock<ISearchResponse<IndexedSite>>();
_client.Setup(x =>
x.SearchAsync<IndexedSite>(It.IsAny<Func<SearchDescriptor<IndexedSite>, ISearchRequest>>(),
default(CancellationToken))).Returns(Task.FromResult(indexedSite.Object));
This works, and it does return indexedSite, however it does not 'Cover' .Index/.Query/.Sort/.Take extension methods, which is what I want.

Per #Olegl answer. It is not possible to Mock extension methods. You need to refactor and get rid of extension methods in order to make it testable
More info here

Related

Moq setup treats all empty enumerables/arrays as the same parameter

I have a method that accepts an IEnumerable:
MyMethod(IEnumerable<MyClass> myParameter)
Now I am writing this code to mock the service:
var array1 = new MyClass[0];
var array2 = new MyClass[0];
_service
.Setup(s => s.MyMethod(array1))
.Returns(value1);
_service
.Setup(s => s.MyMethod(array2))
.Returns(value2);
And finally I am doing two calls to the service with both arrays inside system under test:
_service.MyMethod(array1);
_service.MyMethod(array2);
What I do expect is to get value1 and value2 from these calls, but in practice the latter call overrides the first one and I only get value2 from both calls.
Is this a bug in Moq or is this a feature that setup treats IEnumerable not as a separate object but rather tries to expand it and compare all elements or something (resulting in two empty arrays being the same setup call)?
When you create multiple setups on a method with Moq, each subsequent setup will replace the previous setup unless the setups are conditional (they specify certain conditions on the arguments). See this answer.
You can fix your code by specifying that the arguments must match the ones you intend to pass:
[Test]
public void MyTest()
{
var service = new Mock<MyClass>();
var array1 = new MyClass[0];
var array2 = new MyClass[0];
var value1 = "value1";
var value2 = "value2";
service.Setup(s => s.MyMethod(It.Is<IEnumerable<MyClass>>(e => e == array1))).Returns(value1);
service.Setup(s => s.MyMethod(It.Is<IEnumerable<MyClass>>(e => e == array2))).Returns(value2);
Assert.AreEqual(value1, service.Object.MyMethod(array1));
Assert.AreEqual(value2, service.Object.MyMethod(array2));
}
The behaviour you describe is the default behaviour of the moq, you can see it here. It indeed unfold enumerable and invoke IEnumerable.SequenceEqual. However that is default behaviour(if you setup using an instance, Constant matcher) and you could override it. The one approach is what Owen suggested to use It.Is<T> matcher, e.g.
service.Setup(s => s.MyMethod(It.Is<IEnumerable<MyClass>>(e => e == array1)))
service.Setup(s => s.MyMethod(It.Is<IEnumerable<MyClass>>(e => e == array2)))
Notice that == by default do ReferenceEquals() so this will make different non overridable setups.

How to unit test a repository method that uses 3rd party library in LINQ query?

I am trying to unit test the following method (repo is mocked):
public List<StatusLevelDTO> FindAllTranslated(string locale)
{
var statusLevels = Context.StatusLevels
.IncludeFilter(sl => sl.Translations
.Where(t => t.Locale.Name == locale))
.Where(sl => !sl.Deleted)
.ToList();
return Mapper.Map<List<StatusLevelDTO>>(statusLevels);
}
It uses a 3rd party library called EntityFramework Plus which I believe uses projection to filter included entities.
Now, when I run the whole project, everything is fine, but unit test seems to ignore this method and returns everything.
Is there any way to make IncludeFilter extension method simply do its work?
Mock configuration:
public static DbSet<T> GetMockedDbSet<T>(List<T> sourceList) where T : class
{
var queryableList = sourceList.AsQueryable();
var dbSetMock = new Mock<DbSet<T>>();
dbSetMock.As<IQueryable<T>>().Setup(x => x.GetEnumerator()).Returns(() => queryableList.GetEnumerator());
dbSetMock.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryableList.Provider);
dbSetMock.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryableList.Expression);
dbSetMock.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryableList.ElementType);
dbSetMock.Setup(d => d.Add(It.IsAny<T>())).Callback<T>(s => sourceList.Add(s));
dbSetMock.Setup(d => d.Include(It.IsAny<string>())).Returns(dbSetMock.Object);
return dbSetMock.Object;
}
A possible duplicate, e.g. Mocking Extension Methods with Moq doesn't answer my question as I'm not asking how to mock/stub extension methods. The question is: is it possible to somehow call its logic without mocking/stubbing it?

Mocking FindAsync method

I mocked FindAsync by the following code:
var brands = new Mock<DbSet<Brand>>();
ConfigureTheDbSet(brands, brandData);
brands.Setup(b => b.FindAsync(It.IsAny<object[]>())) //substitution of the .SelectAsync(id) method
.Returns<object[]>(ids => brands.Object.FirstOrDefaultAsync(b => b.BrandId == (int) ids[0]));
and it had been working correctly until I added mocking for AsNoTracking to context:
var mockContext = new Mock<ReportDbContext>();
mockContext.Setup(m => m.Set<Brand>()).Returns(brands.Object);
mockContext.Setup(m => m.Set<Brand>().AsNoTracking()).Returns(brands.Object);
And FindAsync returns null. To make it work i added the following mocking:
mockContext.Setup(m => m.Set<Brand>().FindAsync(It.IsAny<object[]>()))
.Returns<object[]>(async d => await brands.Object.FindAsync(d));
Anybody have a clue why this is happening?
IMO, you should be mocking interfaces, for example IBrandRepository. Otherwise whats the point of mocking? - you could just create an instance of your class, call FindAsync() and assert the result as usual..
Here is how I use Moq with interfaces, for example a repo interface;
// arrange
var mockRepo = new Mock<IBrandRepository>();
mockRepo.Setup(o => o.FindAsync(It.IsAny<string>())).ReturnsAsync(new Brand[] { ... });
var someClass = new SomeClass(IBrandRepository); // someClass that use IBrandRepository
// act
string search = "brand1 brand2"; // what the user searches for
var results = someClass.FindBrands(searchText) // internally calls IBrandRepository.FindAsync()
// assert
// Assert.AreEqual(results.Count(), ...
Dimitry, I know that it's been a couple of years. I just had the same issue and this is what I did to get it to work
this.mocContext.Setup(x => x.Company.FindAsync(It.IsAny<string>
())).Returns(Task.FromResult(this.GetCompanyList().SingleOrDefault(x =>
x.CompanyCode.Equals("M5QoKF4AS0"))));
Here mocContext is my Moq'd Database, Company is the table that I want to perform the FindAsync on, the Get CompanyList just pulls from the data the populated the table. Hopefully this helps
I circumvented this mocking by using .Where(entity => entity.PrimaryKey == key).FirstOrDefaultAsync() in place of .FindAsync(key) as FirstOrDefaultAsync did not require a separate mocking and logic is also same between both approaches
For anyone struggling with this, here's another way of doing it:
First: Create a mock of the DbContext and the DbSet, ex:
private static readonly Mock<ApplicationDbContext> mockDbContext = new();
private static readonly Mock<DbSet<Game>> mockGameSet = new();
Second: Setup the Get of the DbSet in the Mocked DbContext, like this:
mockDbContext.SetupGet(_ => _.Games).Returns(mockGameSet.Object);
Third, and Last: Setup the FindAsync() in the mocked DbSet like this:
mockGameSet.Setup(x => x.FindAsync(It.IsAny<int>())).ReturnsAsync(game);

How do I mock multiple levels of DbSet.Include lambdas?

I'm using Moq to write unit tests that use Entity Framework 6 DbSet and DbContext objects. I have a service method with a cascading/multi-level Include and I can't figure out how to set it up for testing. The service method looks something like this:
return DataContext.Cars
.Include(p => p.Model)
.Include(p => p.Model.Make)
.Select(c => new
{
Key = c.CarId,
Value = string.Format("{0} {1} {2}", c.Model.Make.Name, c.Model.Name, c.Trim)
}
).ToArray();
I know that I have to setup the Include to return the mocked object, like this:
mockCarDbSet.Setup(m => m.Include(It.IsAny<string>())).Returns(mockCarSet.Object);
But I'm getting a null reference exception from the cascaded .Include(p => p.Model.Make). How do I set up Moq to handle multiple levels of Include?
EDIT
OK, so it turns out that I can't use It.IsAny<string> for Include calls that use lambdas instead of strings, so now I have two problems:
How do I setup a mock with Include that accepts a lambda?
Will the setup for above cascade to multiple levels?
include() is a static method(extension method).
Moq doesn't support a static methods mock(read this link).
To test your code you need to set your mockCarDbSet to return IQueryable<Car>:
var carQuery = new List<Car>
{
//add cars
}
IQueryable<Post> query = carQuery.AsQueryable();
return query as a result of DataContext.Cars
Those steps will work around the static method problem.
So thanks to #Old Fox reminding me that Moq won't work with static members, I found a way to do this using Microsoft Fakes. Shims allows you to shim static methods. I used Moq to set up Mock<DbSet> objects for each of the entities:
var carData = new List<Car>{new Car{ Trim = "Whatever" }};
var mockCarSet = new Mock<DbSet<Car>>();
mockCarSet.As<IQueryable<Car>>().Setup(m => m.Provider).Returns(carData.Provider);
mockCarSet.As<IQueryable<Car>>().Setup(m => m.Expression).Returns(carData.Expression);
mockCarSet.As<IQueryable<Car>>().Setup(m => m.ElementType).Returns(carData.ElementType);
mockCarSet.As<IQueryable<Car>>().Setup(m => m.GetEnumerator()).Returns(carData.GetEnumerator);
var mockMakeSet = new Mock<DbSet<Make>>();
//do the same stuff as with Car for IQueryable Setup
var mockModelSet = new Mock<DbSet<Model>>();
//do the same stuff as with Car for IQueryable Setup
using(ShimsContext.Create())
{
//hack to return the first, since this is all mock data anyway
ShimModel.AllInstances.MakeGet = model => mockMakeSet.Object.First();
ShimCar.AllInstances.ModelGet = car => mockModelSet.Object.First();
//run the test
}

Rhino Mocks, assert that a MockRepository was not used (methods)?

Is there a way of asserting that no methods were called in MockRepository?
Say I have:
var repo = MockRepository.GenerateStub<RealRepo>();
I know I can do:
repo.AssertWasNotCalled(...);
But is there a way of checking that it was not used? Instead of doing all the methods everytime i want to check if a repo was not used?
I have cases where I want to just check that I don't use this repo.
Use StrictMock instead of stub:
var repo = MockRepository.GenerateStrictMock<RealRepo>();
It will throw exception if you will try to call any member which do not have setup.
BTW same is true for Moq:
var repoMock = new Mock<RealRepo>(MockBehavior.Strict);
You can try adding your own extension to Rhino Mocks. Something like this:
public static void AssertNothingWasCalled<T>(this T mock)
{
var methodsToVerify = typeof (T)
.GetMethods()
.Where(m => !m.IsSpecialName);
foreach (var method in methodsToVerify)
{
var arguments = BuildArguments(method);
var action = new Action<T>(x => method.Invoke(x, arguments));
mock.AssertWasNotCalled(action, y => y.IgnoreArguments());
}
}
private static object[] BuildArguments(MethodInfo methodInfo)
{
return methodInfo
.GetParameters()
.Select(p => Arg<object>.Is.Anything)
.ToArray();
}
But the answer by Sergey Berezovskiy seems a bit simpler.
Perhaps the easiest thing to do would be to pass a null ref to the calling class. Your SUT should throw a NullReferenceException if it attempts to use it. This is possibly the simplest thing that will work.
However, if the SUT checks for null, then this won't work.

Categories