C# Unit Testing Async Error - c#

Just to set the question, when I have come to write a unit test I hit the following error:
Error: The source IQueryable doesn't implement
IDbAsyncEnumerable
The problem happens when testing a method which calls ToListAsync(), the unit test is as follows:
[TestMethod]
public async Task Method1CallsCount()
{
//arrange
MockContainer container = new MockContainer();
IQueryable<Entity1DTO> querableentity1DTO = new List<Entity1DTO>().AsQueryable();
container.DefaultQueryFactory.Setup(p => p.Load(It.IsAny<ContextEnums>(), It.IsAny<Expression<Func<Entity1, Entity1DTO>>>(),
It.IsAny<Expression<Func<Entity1, bool>>>(), It.IsAny<int>(), It.IsAny<bool>())).Returns(querableentity1DTO);
var manager = new Manager1(container.DefaultQueryFactory.Object);
//act
var result = await manager.Method1();
//assert
//container.repo1.Verify(x => x.repoMethod(It.IsAny<Expression<Func<Entity1,bool>>>()), Times.Once);
}
And here is the method I am testing:
public async Task<List<Entity1DTO>> Method1()
{
Expression<Func<Entity1, Entity1DTO>> select = (x => new Entity1DTO()
{
...
});
Expression<Func<Entity1, bool>> where = (x => x.Property == "Test");
return await _defaultQueryFactory.Load(ContextEnums.Enum1, select, where).ToListAsync();
}
To help a bit, I've tried mocking up the method that loads the data in the query factory and the error appears because the DTO model doesn't implement IDbAsyncEnumerable, now the method that is getting tested sends off a select statement and a where statement and an entity type which the query factory then uses to generate a query this is then executed with ToListAsync() when it returns from the load Method. The error message shows that the DTO model is the one that doesn't implement the IDbAsync not the DB entity itself.
I understand that there are a few other questions out there that are simular but my difference is that I use a DTO model and the method in question does not use the context itself as it is injected into the load method and not in the place in which ToListAsync() is called.
anyone any ideas?

Error happens because Entity Framework async extension methods does not work with any IQueryable - it should also implement IDbAsyncEnumerable interface. Consider this:
var query = new List<EntityDTO>().AsQueryable();
var result = query.ToListAsync().Result;
This will throw the same exception you observe in your code. EnumerableQuery returned by AsQueryable does not implement required interface, so we need to use some other implementation. We can find one in this article (or just create ourselves since it's not hard):
static class TestExtensions {
public static IQueryable<T> AsAsyncQueryable<T>(this IEnumerable<T> source) {
return new TestDbAsyncEnumerable<T>(source);
}
}
internal class TestDbAsyncEnumerable<T> : EnumerableQuery<T>, IDbAsyncEnumerable<T>
{
public TestDbAsyncEnumerable(IEnumerable<T> enumerable)
: base(enumerable)
{ }
public TestDbAsyncEnumerable(Expression expression)
: base(expression)
{ }
public IDbAsyncEnumerator<T> GetAsyncEnumerator()
{
return new TestDbAsyncEnumerator<T>(this.AsEnumerable().GetEnumerator());
}
IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator()
{
return GetAsyncEnumerator();
}
}
internal class TestDbAsyncEnumerator<T> : IDbAsyncEnumerator<T> {
private readonly IEnumerator<T> _inner;
public TestDbAsyncEnumerator(IEnumerator<T> inner) {
_inner = inner;
}
public void Dispose() {
_inner.Dispose();
}
public Task<bool> MoveNextAsync(CancellationToken cancellationToken) {
return Task.FromResult(_inner.MoveNext());
}
public T Current => _inner.Current;
object IDbAsyncEnumerator.Current => Current;
}
Now we can do:
IQueryable<Entity1DTO> querableentity1DTO = new List<Entity1DTO>().AsAsyncQueryable();
And EF async methods will execute correctly on it.

There are actually two problems here, firstly you do need to use the solution in the other answer and change the return type to TestDbAsyncEnumerator and the other problem is to do with Moq, the setup needs to have the same parameters called, so in your case you have
return await _defaultQueryFactory.Load(ContextEnums.Enum1, select, where).ToListAsync();
and
container.DefaultQueryFactory.Setup(p => p.Load(It.IsAny<ContextEnums>(), It.IsAny<Expression<Func<Entity1, Entity1DTO>>>(),
It.IsAny<Expression<Func<Entity1, bool>>>(), It.IsAny<int>(), It.IsAny<bool>())).Returns(querableentity1DTO);
notice how you have two extra parameters on the end, try leaving them as null. That should sort out the problem, just to make sure try adding an item to the return list as well.

Related

How to return DbRawSqlQuery in Moq setup method

I have a procedure service which contain only methods like this one:
DbRawSqlQuery<UserVesselPermissionsResult> GetUserVesselPermissions(Guid userId, DateTime date);
So, all the methods are returning DbRawSqlQuery, then in some upper layer of the application I turn them into IEnumerable. But for the testing purpose in some places I have to setup this method. The problem is that the class DbRawSqlQuery does have a internal constructor(I know Moq does not accept internal constructors) but I dont know if there is some way to make this code work:
_procedureService.Setup(x => x.GetUserVesselPermissions(It.IsAny<Guid>(), It.IsAny<DateTime>()))
.Returns(new DbRawSqlQuery<UserVesselPermissionsResult>(null));
Currently it does not work due to DbRawSqlQuery which can not be instantiated easily.
EDIT 1:
Here are some more details:
public class IMembershipService
{
private readonly IProcedureService _procedureService;
public MembershipService(IProcedureService procedureService)
{
_procedureService = procedureService;
}
public List<UserVesselPermissionsResult> UserPermissions => _procedureService.GetUserVesselPermissions(UserId, DateTime.Now).ToList();
public bool UserHasPermissionOrAdmin(YcoEnum.UIPermission permission)
{
if (IsUserAdministrator)
return true;
var userVesselPermissions = UserVesselPermissions; //Here I have to make the setup
if (userVesselPermissions == null)
return false;
var userSelectedVesselId = UserSelectedVesselId;
return //something
}
}
The test method would look like this:
[TestCase(true)]
[TestCase(false)]
public void UserHasAllPermissionsOrAdmin_IsAdminOrNot_ReturnsTrue(bool isAdmin)
{
//Arrange
_membershipService.IsUserAdministrator = isAdmin;
var claims = new List<Claim>()
{
new Claim(ClaimTypes.Name, "rajmondi#outlook.com"),
new Claim(ClaimTypes.NameIdentifier, Guid.NewGuid().ToString())
};
var identity = new ClaimsIdentity(claims, "TestAuthType");
var claimsPrincipal = new ClaimsPrincipal(identity);
_authenticationManager.Setup(x => x.User).Returns(claimsPrincipal);
_procedureService.Setup(x => x.GetUserVesselPermissions(It.IsAny<Guid>(), It.IsAny<DateTime>()))
.Returns((DbRawSqlQuery<UserVesselPermissionsResult>) null);//Here I dont know how to set it up due to DbRawSqlQuery
//Action
var result = _membershipService.UserHasAllPermissions(It.IsAny<YcoEnum.UIPermission>());
//Assert
Assert.That(result, Is.EqualTo(true));
}
Any help is much appreciated!
Cheers!
I could make it work, I actually did not like the idea of changing the whole IProcedureService just because it does return a built-int type from entity framework. Since I get the data from the procedure service and return them to IEnumerable I only had to care for GetEnumerator() method, so what I thought would be to check first how the code was constructed inside, I found that DbSqlQuery was inheriting from DbRawSqlQuery and did not have the problem of internal constructor. In this case I created a new class called TestDbSqlQuery which would inherit from the DbSqlQuery. The class look like this:
public class TestDbSqlQuery<T> : DbSqlQuery<T> where T : class
{
private readonly List<T> _innerList;
public TestDbSqlQuery(List<T> innerList)
{
_innerList = innerList;
}
public override IEnumerator<T> GetEnumerator()
{
return _innerList.GetEnumerator();
}
}
I added purposely the Lis<T> as a param so I can store my data in that list and then use my overriden IEnumerator<T>.
So, now the test method would be like this:
_procedureService.Setup(x => x.GetUserVesselPermissions(It.IsAny<Guid>(), It.IsAny<DateTime>()))
.Returns(new TestDbSqlQuery<UserVesselPermissionsResult>(new List<UserVesselPermissionsResult>
{
new UserVesselPermissionsResult
{
PermissionId = 1
}
}));
and it is working fine, at the end the TestDbSqlQuery can be modified as needed but the idea is the same, just store the objects in some container and then retrieve them in GetEnumerator method.

Moq with Func<Foo, Task<List<Bar>>>>

Scenario:
We are in the lovely scenario of a terrible data source that requires an arcane syntax. We have built our "repository" layer to translate simple parameters (primitive values) into the correct syntax for the destination.
We would like to unit test that:
The correct filters have been applied (can be done by checking the string that the repo's helper methods create)
The (mocked) remote data source is called exactly once
The (mocked) data we have defined the remote data source as returning is passed back as the return value when we call the repo.
For example
var expectedReturn = new List<Product> { new Product { StockNumber = "123" } };
provider.Setup(x => x.Run(It.IsAny<Func<IRemoteClient, Task<List<Product>>>>(),
It.IsAny<string>())).ReturnsAsync(expectedReturn);
Moq is failing on the Setup line with a NotSupportedException. I've read probably a dozen or more SO posts and can't find out why it doesn't work.
In normal usage, the Repo will use something like:
provider.Run(x => x.GetAsync<List<Product>>(requestBuilder.Request), "foo")
Definition of run in provider interface:
Task<T> Run<T>(Func<IRemoteClient, Task<T>> action, string name);
Since the requestBuilder is injected as well, we can easily evaluate that the request is built correctly as far as the number and type of parameters, but we can't run the test at all because the Mock call fails the setup and so we never get there.
I am using Moq 4.9.0 and have tested this both on .NET Core 2.1, as well as inside LINQPad using the .NET Framework. It compiles and runs for me without any problems. I am able to run the mock setup, and I am also able to call the mocked method on the mock object, and retrieve the expected return result.
The following is my test code:
class Program
{
static void Main(string[] args)
{
var expectedReturn = new List<Product> { new Product { StockNumber = "123" } };
var provider = new Mock<IProvider>();
provider
.Setup(x => x.Run(
It.IsAny<Func<IRemoteClient, Task<List<Product>>>>(),
It.IsAny<string>()))
.ReturnsAsync(expectedReturn);
var result = provider.Object.Run(client => client.GetAsync<List<Product>>(null), "foo");
Console.WriteLine(result.Result[0].StockNumber);
}
}
public interface IProvider
{
Task<T> Run<T>(Func<IRemoteClient, Task<T>> action, string name);
}
public interface IRemoteClient
{
Task<T> GetAsync<T>(object request);
}
public class Product
{
public string StockNumber { get; set; }
}

Cant Mock my Get() function from my repository - MongoDB.Driver 2.2.3

In my unit test I want to test my method that I created for filtering data from MongoDB.
When I try to mock my function like this:
_repo.GetFluent<Location>((Arg.Any<Expression<Func<Location, bool>>>()))
.Returns(x => locations.Where(x.Arg<Expression<Func<Location, bool>>>()).ToList());
It underlines the Returns saying:
Cannot convert lambda expression.
Before when I worked on my simple project using the 2.0.0 MongoDB driver I had no problem mocking my Get() function like this, but now with the new 2.2.3 driver I have an error mocking this. Is there another way?
I've seen that the new driver is using IFindFluent and the older one I used the MongoCursor to get my data.
Should I mock the IFindFluent somehow?
This is my code for the GetFluent() method
public IFindFluent<TEntity, TEntity> GetFluent<TEntity>(System.Linq.Expressions.Expression<Func<TEntity, bool>> filter = null) where TEntity : class, new()
{
var collection = GetCollection<TEntity>();
if (filter == null)
{
var emptyFilter = Builders<TEntity>.Filter.Empty;
return collection.Find(emptyFilter);
}
else
{
var filterDefinition = Builders<TEntity>.Filter.Where(filter);
return collection.Find(filterDefinition);
}
}
Yes, you need to mock IFindFluent. Let me show you an example.
I used NUnit and Moq for tests, driver version is 2.2.3.
public interface IRepository
{
IFindFluent<TEntity, TEntity> GetFluent<TEntity>(Expression<Func<TEntity, bool>> filter = null)
where TEntity : class, new();
}
public class LocationService
{
public long CountLocations(IRepository repository)
{
return repository.GetFluent<Location>(location => true).Count();
}
}
[TestFixture]
public class LocationServiceTests
{
[Test]
public void CountLocationsTest()
{
const long LocationCount = 5;
var locationsMock = new Mock<IFindFluent<Location, Location>>();
locationsMock.Setup(x => x.Count(default(CancellationToken))).Returns(LocationCount);
var repoMock = new Mock<IRepository>();
repoMock.Setup(repo => repo.GetFluent(It.IsAny<Expression<Func<Location, bool>>>()))
.Returns(locationsMock.Object);
var locationService = new LocationService();
long result = locationService.CountLocations(repoMock.Object);
Assert.AreEqual(LocationCount, result);
}
}
Good answer from Usein Mambediev. There is an similar example, how to mock IFindFluent with Typemock Isolator without wrapping it into the interface:
[TestMethod, Isolated]
public void TestGet()
{
var target = new ClassUnderTest();
var returnMock = Isolate.Fake.Instance<IFindFluent<Location, Location>>();
int size = 3;
Isolate.WhenCalled(() => returnMock.Count()).WillReturn(size);
Isolate.WhenCalled(() => target.GetFluent(default(Expression<Func<Location, bool>>))).WillReturn(returnMock);
Assert.AreEqual(size, target.GetFluent<Location>(location => true).Count());
}
I've put your method into the public class just in order to test. You only need to change the target.
Good luck!

Moq Method called by System.Threading.Tasks.Task.Factory.StartNew

I am using Moq to write a unit test. I have a DataManager object which calls WCF to fetch data. I inject this into my controller. however inside the controller the call to the Method in this DataManager is wrapped inside of a Task
System.Threading.Tasks.Task.Factory.StartNew<MyDataObject>(()=>
{
return DataManager.GetMyDataObject(userobj, recordid);
}
I have created a mock for the DataManager.GetMyDataObject with Moq
but whenever it is called from this statement inside of the controller method
it returns null. I have googled alot but most of the stuff out there are dealing with methods which have Task as the return signature.
The DataManager.GetMyDataObject is written as standard sync code.
I am using Moq v4.0.10827 and doubt I can upgrade.
I am trying many ways..Moq seems to expect the return to match the method signature
_mockDataManager = new Mock<_mockDataManager>();
_mockDataManager.Setup(m => m.GetMyDataObject(It.IsAny<UserObj>(), It.IsAny<Guid>()))
and well then returns? I also trid callback
_mockDataManager.Setup(m => System.Threading.Tasks.Task.FromResult(m.GetMyDataObject(It.IsAny<UserObj>(), It.IsAny<Guid>())
.Returns(System.Threading.Tasks.Task.FromResult(myData))
.Callback<MyDataObject>(o => myData = o);
myData = GetMyDataObject();
_mockDataManager.Setup(m => m.GetMyDataObject(It.IsAny<UserObj>(), It.IsAny<Guid>()).Returns(GetMyDataObject())
private GetMyDataObject() {
returns new DataSet(); //basically an empty dataset but not null
}
Given the following classes:
public class MyDataObject { }
public class UserObj { }
public class DataManager
{
public virtual MyDataObject GetMyDataObject(UserObj userObj, Guid guid)
{
throw new NotImplementedException();
}
}
class SUT
{
public DataManager DataManager { get; private set; }
public SUT(DataManager dataManager)
{
DataManager = dataManager;
}
public void Method(UserObj userobj, Guid recordid)
{
var t = System.Threading.Tasks.Task.Factory.StartNew<MyDataObject>(()=>
{
return DataManager.GetMyDataObject(userobj, recordid);
});
t.Wait();
}
}
the following mock works fine:
var mockDataManager = new Mock<DataManager>();
mockDataManager.Setup(m => m.GetMyDataObject(It.IsAny<UserObj>(), It.IsAny<Guid>()));
var sut = new SUT(mockDataManager.Object);
sut.Method(new UserObj(), Guid.Empty);
mockDataManager.VerifyAll();
Two pitfalls:
In the code you posted, you use
_mockDataManager = new Mock<_mockDataManager>();
which should be
_mockDataManager = new Mock<DataManager>(); // or whatever the name of the class is
Maybe this is just a copy/paste error, maybe not.
Also, since you use a Task here:
System.Threading.Tasks.Task.Factory.StartNew<MyDataObject>(()=>
{
return DataManager.GetMyDataObject(userobj, recordid);
}
which calls GetMyDataObject on DataManager, you have to make sure that the Task finished before you verify your mock setup. If you would remove the t.Wait(); from my code above, the test would fail, because VerifyAll would be called before the Task would start and call GetMyDataObject in the mocked object.

Manipulating objects with DbSet<T> and IQueryable<T> with NSubstitute returns error

I'd like to use NSubstitute to unit test Entity Framework 6.x by mocking DbSet. Fortunately, Scott Xu provides a good unit testing library, EntityFramework.Testing.Moq using Moq. So, I modified his code to be suitable for NSubstitute and it's been looking good so far, until I wanted to test DbSet<T>.Add(), DbSet<T>.Remove() methods. Here's my code bits:
public static class NSubstituteDbSetExtensions
{
public static DbSet<TEntity> SetupData<TEntity>(this DbSet<TEntity> dbset, ICollection<TEntity> data = null, Func<object[], TEntity> find = null) where TEntity : class
{
data = data ?? new List<TEntity>();
find = find ?? (o => null);
var query = new InMemoryAsyncQueryable<TEntity>(data.AsQueryable());
((IQueryable<TEntity>)dbset).Provider.Returns(query.Provider);
((IQueryable<TEntity>)dbset).Expression.Returns(query.Expression);
((IQueryable<TEntity>)dbset).ElementType.Returns(query.ElementType);
((IQueryable<TEntity>)dbset).GetEnumerator().Returns(query.GetEnumerator());
#if !NET40
((IDbAsyncEnumerable<TEntity>)dbset).GetAsyncEnumerator().Returns(new InMemoryDbAsyncEnumerator<TEntity>(query.GetEnumerator()));
((IQueryable<TEntity>)dbset).Provider.Returns(query.Provider);
#endif
...
dbset.Remove(Arg.Do<TEntity>(entity =>
{
data.Remove(entity);
dbset.SetupData(data, find);
}));
...
dbset.Add(Arg.Do<TEntity>(entity =>
{
data.Add(entity);
dbset.SetupData(data, find);
});
...
return dbset;
}
}
And I created a test method like:
[TestClass]
public class ManipulationTests
{
[TestMethod]
public void Can_remove_set()
{
var blog = new Blog();
var data = new List<Blog> { blog };
var set = Substitute.For<DbSet<Blog>, IQueryable<Blog>, IDbAsyncEnumerable<Blog>>()
.SetupData(data);
set.Remove(blog);
var result = set.ToList();
Assert.AreEqual(0, result.Count);
}
}
public class Blog
{
...
}
The issue arises when the test method calls set.Remove(blog). It throws an InvalidOperationException with error message of
Collection was modified; enumeration operation may not execute.
This is because the fake data object has been modified when the set.Remove(blog) method is called. However, the original Scott's way using Moq doesn't result in the issue.
Therefore, I wrapped the set.Remove(blog) method with a try ... catch (InvalidOperationException ex) block and let the catch block do nothing, then the test doesn't throw an exception (of course) and does get passed as expected.
I know this is not the solution, but how can I achieve my goal to unit test DbSet<T>.Add() and DbSet<T>.Remove() methods?
What's happening here?
set.Remove(blog); - this calls the previously configured lambda.
data.Remove(entity); - The item is removed from the list.
dbset.SetupData(data, find); - We call SetupData again, to reconfigure the Substitute with the new list.
SetupData runs...
In there, dbSetup.Remove is being called, in order to reconfigure what happens when Remove is called next time.
Okay, we have a problem here. dtSetup.Remove(Arg.Do<T.... doesn't reconfigure anything, it rather adds a behavior to the Substitute's internal list of things that should happen when you call Remove. So we're currently running the previously configured Remove action (1) and at the same time, down the stack, we're adding an action to the list (5). When the stack returns and the iterator looks for the next action to call, the underlying list of mocked actions has changed. Iterators don't like changes.
This leads to the conclusion: We can't modify what a Substitute does while one of its mocked actions is running. If you think about it, nobody who reads your test would assume this to happen, so you shouldn't do this at all.
How can we fix it?
public static DbSet<TEntity> SetupData<TEntity>(
this DbSet<TEntity> dbset,
ICollection<TEntity> data = null,
Func<object[], TEntity> find = null) where TEntity : class
{
data = data ?? new List<TEntity>();
find = find ?? (o => null);
Func<IQueryable<TEntity>> getQuery = () => new InMemoryAsyncQueryable<TEntity>(data.AsQueryable());
((IQueryable<TEntity>) dbset).Provider.Returns(info => getQuery().Provider);
((IQueryable<TEntity>) dbset).Expression.Returns(info => getQuery().Expression);
((IQueryable<TEntity>) dbset).ElementType.Returns(info => getQuery().ElementType);
((IQueryable<TEntity>) dbset).GetEnumerator().Returns(info => getQuery().GetEnumerator());
#if !NET40
((IDbAsyncEnumerable<TEntity>) dbset).GetAsyncEnumerator()
.Returns(info => new InMemoryDbAsyncEnumerator<TEntity>(getQuery().GetEnumerator()));
((IQueryable<TEntity>) dbset).Provider.Returns(info => getQuery().Provider);
#endif
dbset.Remove(Arg.Do<TEntity>(entity => data.Remove(entity)));
dbset.Add(Arg.Do<TEntity>(entity => data.Add(entity)));
return dbset;
}
The getQuery lambda creates a new query. It always uses the captured list data.
All .Returns configuration calls use a lambda. In there, we create a new query instance and delegate our call there.
Remove and Add only modify our captured list. We don't have to reconfigure our Substitute, because every call reevaluates the query using the lambda expressions.
While I really like NSubstitute, I would strongly recommend looking into Effort, the Entity Framework Unit Testing Tool.
You would use it like this:
// DbContext needs additional constructor:
public class MyDbContext : DbContext
{
public MyDbContext(DbConnection connection)
: base(connection, true)
{
}
}
// Usage:
DbConnection connection = Effort.DbConnectionFactory.CreateTransient();
MyDbContext context = new MyDbContext(connection);
And there you have an actual DbContext that you can use with everything that Entity Framework gives you, including migrations, using a fast in-memory-database.

Categories