I am writing a Unit Test and need to mock Entity Framework's .FromSqlRaw method. When the method is executed in the class under test, it throws following exception:
System.InvalidOperationException: There is no method
'FromSqlOnQueryable' on type
'Microsoft.EntityFrameworkCore.RelationalQueryableExtensions' that
matches the specified arguments.
Following is class under test:
public class PowerConsumptionRepository : IPowerConsumptionRepository
{
private readonly IDatabaseContext _databaseContext;
private readonly IDateTimeHelper _dateTimeHelper;
public PowerConsumptionRepository(IDatabaseContext databaseContext, IDateTimeHelper dateTimeHelper)
{
_databaseContext = databaseContext;
_dateTimeHelper = dateTimeHelper;
}
public List<IntervalCategoryConsumptionModel> GetCurrentPowerConsumption(string siteId)
{
var currentDate = _dateTimeHelper
.ConvertUtcToLocalDateTime(DateTime.UtcNow, ApplicationConstants.LocalTimeZone)
.ToString("yyyy-MM-dd");
var currentDateParameter = new SqlParameter("currentDate", currentDate);
var measurements = _databaseContext.IntervalPowerConsumptions
.FromSqlRaw(SqlQuery.CurrentIntervalPowerConsumption, currentDateParameter)
.AsNoTracking()
.ToList();
return measurements;
}
}
Unit Test:
public class PowerConsumptionRepositoryTests
{
[Fact]
public void TestTest()
{
var data = new List<IntervalCategoryConsumptionModel>
{
new IntervalCategoryConsumptionModel
{
Id = 1,
Hvac = 10
},
new IntervalCategoryConsumptionModel
{
Id = 1,
Hvac = 10
}
}.AsQueryable();
var dateTimeHelper = Substitute.For<IDateTimeHelper>();
dateTimeHelper.ConvertUtcToLocalDateTime(Arg.Any<DateTime>(), Arg.Any<string>()).Returns(DateTime.Now);
var mockSet = Substitute.For<DbSet<IntervalCategoryConsumptionModel>, IQueryable<IntervalCategoryConsumptionModel>>();
((IQueryable<IntervalCategoryConsumptionModel>)mockSet).Provider.Returns(data.Provider);
((IQueryable<IntervalCategoryConsumptionModel>)mockSet).Expression.Returns(data.Expression);
((IQueryable<IntervalCategoryConsumptionModel>)mockSet).ElementType.Returns(data.ElementType);
((IQueryable<IntervalCategoryConsumptionModel>)mockSet).GetEnumerator().Returns(data.GetEnumerator());
var context = Substitute.For<IDatabaseContext>();
context.IntervalPowerConsumptions = (mockSet);
var repo = new PowerConsumptionRepository(context, dateTimeHelper);
var result = repo.GetCurrentPowerConsumption(Arg.Any<string>());
result.Should().NotBeNull();
}
}
In my scenario I use FromSqlRaw method for invoke stored procedure in my database.
For EntityFramework Core (version 3.1 works well for sure) I do it in this way:
Add virtual method to your DbContext class:
public virtual IQueryable<TEntity> RunSql<TEntity>(string sql, params object[] parameters) where TEntity : class
{
return this.Set<TEntity>().FromSqlRaw(sql, parameters);
}
It's just a simple virtaul wraper from static FromSqlRaw, so you can easily mock it:
var dbMock = new Mock<YourContext>();
var tableContent = new List<YourTable>()
{
new YourTable() { Id = 1, Name = "Foo" },
new YourTable() { Id = 2, Name = "Bar" },
}.AsAsyncQueryable();
dbMock.Setup(_ => _.RunSql<YourTable>(It.IsAny<string>(), It.IsAny<object[]>())).Returns(tableContent );
Call our new RunSql method instead of FromSqlRaw:
// Before
//var resut = dbContext.FromSqlRaw<YourTable>("SELECT * FROM public.stored_procedure({0}, {1})", 4, 5).ToListAsync();
// New
var result = dbContext.RunSql<YourTable>("SELECT * FROM public.stored_procedure({0}, {1})", 4, 5).ToListAsync();
Last, but not least, you need to add AsAsyncQueryable() extension method to your test project. It's provided by user #vladimir in a brilliant answer here:
public static class QueryableExtensions
{
public static IQueryable<T> AsAsyncQueryable<T>(this IEnumerable<T> input)
{
return new NotInDbSet<T>( input );
}
}
public class NotInDbSet< T > : IQueryable<T>, IAsyncEnumerable< T >, IEnumerable< T >, IEnumerable
{
private readonly List< T > _innerCollection;
public NotInDbSet( IEnumerable< T > innerCollection )
{
_innerCollection = innerCollection.ToList();
}
public IAsyncEnumerator< T > GetAsyncEnumerator( CancellationToken cancellationToken = new CancellationToken() )
{
return new AsyncEnumerator( GetEnumerator() );
}
public IEnumerator< T > GetEnumerator()
{
return _innerCollection.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public class AsyncEnumerator : IAsyncEnumerator< T >
{
private readonly IEnumerator< T > _enumerator;
public AsyncEnumerator( IEnumerator< T > enumerator )
{
_enumerator = enumerator;
}
public ValueTask DisposeAsync()
{
return new ValueTask();
}
public ValueTask< bool > MoveNextAsync()
{
return new ValueTask< bool >( _enumerator.MoveNext() );
}
public T Current => _enumerator.Current;
}
public Type ElementType => typeof( T );
public Expression Expression => Expression.Empty();
public IQueryProvider Provider => new EnumerableQuery<T>( Expression );
}
With .FromSqlRaw you are sending raw sql query to the database engine.
If you really want to test that your application (.FromsqlRaw) works as expected, test it against an actual database.
Yes it is slower, yes it requires running database with some test data - and yes it will provide you strong confidence that your application is working.
All other tests (mocked or in-memory or sqlite) will provide you false feeling of confidence.
The in-memory provider can't do it as it's a relational operation. Ignoring the philosophical side of it there are probably a couple of ways you could solve it.
Mocking the query provider
Under the covers it's runs through the IQueryProvider.CreateQuery<T>(Expression expression) method so you can use a mocking framework to intercept the invocation and return what you want. That's how EntityFrameworkCore.Testing (disclaimer I am the author) does it. This is how I unit test FromSql* invocations in my code.
A better in-memory provider
I haven't used it much but my understanding is a provider like SQLite may support it.
To address the OP comments, WRT whether you should be using an in-memory provider/mocking the DbContext, we are in the realm of personal opinion. Mine is that I have no reservations using the in-memory provider, it's easy to use, reasonably fast and works well for many. I do agree that you shouldn't mock the DbContext, simply because it'd be really hard to do. EntityFrameworkCore.Testing doesn't mock the DbContext per se, it wraps over an in-memory provider and uses popular mocking frameworks to provide support for things like FromSql* and ExecuteSql*.
I read the linked article by Jimmy Bogard (who I have the utmost respect for), however on this topic I don't agree on all points. On the rare occasion that I have raw SQL in my data access layer, it's generally to invoke a stored procedure or function which already has been tested/has tests outside of my SUT. I generally treat them as a dependency; I should be able to write my unit tests for my SUT with that dependency returning the values required to adequately test my SUT.
Related
We have 22 locations, and each location has its own database or db schema. So, each location has 2 dbs, one oracle db and SQL db.
Now, the way I'm approaching this is to get the user location based on their AD profile when they login to my application and then I can pull the connection string like that.
on program.cs, add reference to my Data layer and add
builder.Services.AddScoped<IProductDataService,ProductDataService>();
builder.Services.AddScoped<IOracleDataAccess, OracleDataAccess>();
builder.Services.AddScoped<ISQLDataAccess, SQLDataAccess>();
ProductDataService.cs
public async Task<List<Product>> GetProducts(string userLocationId)
{
//read query for product by location.
var productQuery= QueryReader.ReadQueryFile(QuerySubFolderName, "products.sql");
var locationData = await _locationService.GetLocationById(userLocationId);
//get data using dapper
//pass the connectionstring based on user AD location.
var data = await _oracleDb.LoadData<Product, dynamic>(productQuery, new { }, locationData.WMSDB);
return data;
}
on my DATA project.
public async Task<List<T>> LoadData<T, U>(string query, U parameters, string connectionStringName)
{
var connectionString = _config.GetConnectionString(connectionStringName);
using (IDbConnection connection = new OracleConnection(connectionString))
{
var items = await connection.QueryAsync<T>(query, parameters, commandType: CommandType.Text, commandTimeout: 240);
return items.ToList();
}
}
While this works, I wonder if this is the right approach. All samples online are usually for using Singleton with one database but in my case, I have 22 databases to connect dynamically. What is the right way to do it?
First, I'll give my single-opinion on this code:
var connectionString = _config.GetConnectionString(connectionStringName);
This is very "Dot-Net-FRAMEWORK'ish", not "DotNet-CORE" thinking.
DotNetCore... the switchover is to "inject DbContexts", not (old FW thinking) of "go lookup a connection string".
Some will disagree.
But there is a reason why "AddDbContext" exists. (Note for EF-CORE, it is a "since core-1.0" and has no "DotNet-FW" versions (if you follow the link below).
https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.entityframeworkservicecollectionextensions.adddbcontext?view=efcore-6.0
So my answer is "AddDbContext" centric. Now "lookup connection string" centric. Please add a tag to your question to disambiguate between dotnet-FRAMEWORK vs dotnet-CORE.
Typically (with ONE database/database-context)..... you have something like this:
public class DepartmentCommandEntityFrameworkDomainDataLayer : IDepartmentCommandDomainData
{
private readonly MyAppNameDbContext entityDbContext;
public DepartmentCommandEntityFrameworkDomainDataLayer(
MyAppNameDbContext context )
{
this.entityDbContext = context ?? throw new ArgumentNullException(
"MyAppNameDbContext is null",
(Exception)null);
}
public async Task<int> AddAsync(DepartmentEntity entity, CancellationToken token)
{
this.entityDbContext.Departments.Add(entity);
int saveChangesAsyncValue = await this.entityDbContext.SaveChangesAsync(token);
return saveChangesAsyncValue;
}
And you inject a SINGLE MyAppNameDbContext into your DAL class.
But you have ~many MyAppNameDbContext's.
So you can go to a "Factory" way......and you inject your many MyAppNameDbContext's into your Factory.
public interface IDbContextFactory()
{
public MyAppNameDbContext GetASingleMyAppNameDbContext(int-or-string factoryKey);
}
and
public class MyDbContextsFactoryConcrete : IDbContextsFactory
{
private readonly IEnumerable<MyAppNameDbContext> allAvailableDbContexts;
public MyDbContextsFactoryConcrete(IEnumerable<MyAppNameDbContext> allAvailableDbContexts)
{
/* check for null or !Any() here and throw an exception, aka, fail early */
this.allAvailableDbContexts = allAvailableDbContexts;
}
public MyAppNameDbContext GetASingleMyAppNameDbContext(int-or-string factoryKey)
{
MyAppNameDbContext returnItem = this.allAvailableDbContexts.(whatever code to find the matching MyAppNameDbContext from the IEnumerable-allAvailableDbContexts);
/* see https://stackoverflow.com/a/52435195/214977 for an idea here */
return returnItem;
}
}
and you will inject ALL available MyAppNameDbContext(s)" into this "Factory".
Now you will refactor your DAL class:
public class DepartmentCommandEntityFrameworkDomainDataLayer : IDepartmentCommandDomainData
{
private readonly IDbContextsFactory entityDbContextFactory;
public DepartmentCommandEntityFrameworkDomainDataLayer(
IDbContextsFactory entityDbContextFactory
) {
this.entityDbContextFactory = entityDbContextFactory ?? throw new ArgumentNullException(
"IDbContextsFactory is null",
(Exception)null);
}
public async Task<int> AddAsync(
int dbContextFactoryKey,
DepartmentEntity entity, CancellationToken token)
{
MyAppNameDbContext foundContext = this.entityDbContextFactory.GetASingleMyAppNameDbContext(dbContextFactoryKey);
foundContext.Departments.Add(entity);
int saveChangesAsyncValue = await this.entityDbContext.SaveChangesAsync(token);
return saveChangesAsyncValue;
}
}
Now, the above is the "idea".
The "match your factoryKey" to "what is in the IEnumerable-collection" needs to be determined.
Here is how I did it one time:
https://stackoverflow.com/a/52435195/214977
If you follow the link to the other SOF answer... you'll see this code.
private IShipper FindIShipper(String preferredShipperAbbreviation)
{
IShipper foundShipper = this.shippers.FirstOrDefault(s => s.FriendlyNameInstance.Equals(preferredShipperAbbreviation, StringComparison.OrdinalIgnoreCase));
if (null == foundShipper)
{
throw new ArgumentNullException(
String.Format("ShipperInterface not found in shipperProviderMap. ('{0}')", preferredShipperAbbreviation));
}
return foundShipper;
}
In the other SOF answer, this code is "buried" inside the OrderProcessor.
The "Factory" guidelines I provide above is basically "encapsulate the 'private IShipper FindIShipper' functionality into its own class to provider better reuse.
I have IDataService that contains generic crud operations
public interface IDataService<T>
{
Task<IEnumerable<T>> GetAll();
Task<IEnumerable<T>> GetAll(string[] includes = null);
Task<T> GetById(int id);
Task<T> Create(T entity);
Task<T> Update(int id, T entity);
Task<bool> Delete(int id);
}
and I have class GenericDataService<T> that implements the IDataService interface:
public class GenericDataService<T> : IDataService<T> where T : DomainObject
{
private readonly DeployToolDBContexFactory _contexFactory;
public GenericDataService(DeployToolDBContexFactory contexFactory)
{
_contexFactory = contexFactory;
}
public async Task<T> Create(T entity)
{
using (DeployToolDBContex contex = _contexFactory.CreateDbContext())
{
EntityEntry<T> createdResult = await contex.Set<T>().AddAsync(entity);
await contex.SaveChangesAsync();
return createdResult.Entity;
}
}
public async Task<bool> Delete(int id)
{
using (DeployToolDBContex contex = _contexFactory.CreateDbContext())
{
T entity = await contex.Set<T>().FirstOrDefaultAsync((e) => e.Id == id);
contex.Set<T>().Remove(entity);
await contex.SaveChangesAsync();
return true;
}
}
public async Task<IEnumerable<T>> GetAll()
{
using (DeployToolDBContex contex = _contexFactory.CreateDbContext())
{
IEnumerable<T> entities = await contex.Set<T>().ToListAsync();
return entities;
}
}
public async Task<T> GetById(int id)
{
using (DeployToolDBContex contex = _contexFactory.CreateDbContext())
{
T entity = await contex.Set<T>().FirstOrDefaultAsync((e) => e.Id == id);
return entity;
}
}
public async Task<T> Update(int id, T entity)
{
using (DeployToolDBContex contex = _contexFactory.CreateDbContext())
{
entity.Id = id;
contex.Set<T>().Update(entity);
await contex.SaveChangesAsync();
return entity;
}
}
public async Task<IEnumerable<T>> GetAll(string[] includes = null)
{
using (DeployToolDBContex contex = _contexFactory.CreateDbContext())
{
var query = contex.Set<T>().AsQueryable();
foreach (var include in includes)
query = query.Include(include);
return query.ToList();
}
}
}
To create objects I'm using a data store class that executes operations with the DataService object:
public class DataStore
{
private static DataStore instance = null;
public static DataStore Instance
{
get
{
instance = instance == null ? new DataStore() : instance;
return instance;
}
}
public IDataService<User> userDataService;
public DataStore()
{
this.userDataService = new GenericDataService<User>(new DeployToolDBContexFactory());
}
}
For example, user creation :
private async void AddUser()
{
User user = new User()
{
UserName = UserNameTxt,
RoleId = RoleSelected.Id
};
await DataStore.Instance.userDataService.Create(user);
}
I'm new to Moq and I want to write unit tests, for example, test of user creation
[Test]
public async Task CreateUser()
{
Mock<DeployToolDBContexFactory> dbContextFactory = new Mock<DeployToolDBContexFactory>();
Mock<DeployToolDBContex> dbContextMock = new Mock<DeployToolDBContex>(dbContextFactory.Object);
var user = new User()
{
UserName = "testName"
};
var mock = new Mock<GenericDataService<User>>(new DeployToolDBContexFactory());
mock.Setup(m => m.Create(user)).ReturnsAsync(new User() { UserName = user.UserName}).Verifiable();
var service = new GenericDataService<User>(dbContextFactory.Object);
User u = await service.Create(user);
Assert.AreEqual(u.UserName , user.UserName);
}
I'm getting this error :
System.NotSupportedException : Unsupported expression: m => m.Create(user)
Non-overridable members (here: GenericDataService.Create) may not be used in setup / verification expressions.
I tried to set DbSet as virtual.
Thanks for any help.
It looks like you are a bit mixed up with mocking and what exactly you should be testing. Your GenericDataService is essentially a Generic Repository pattern. This is actually an anti-pattern for EF, but there is plenty to read up on why you shouldn't use it with EF... Repository patterns in general are a good thing for facilitating unit testing, however this is because they serve as a boundary to avoid needing to try and mock a DbContext and its DbSets.
Firstly, your Test is testing the wrong thing. The test should be testing whatever method that will be calling your AddUser method. Whether this is in a Controller or a Service, etc. That controller would have a dependency of IDataService<User> declared which we would be mocking in order to test the controller:
For the sake of argument I've made AddUser() a public method. In your case you should have a public action or method that calls AddUser and would set up a test for that method. You should also structure your code to avoid being dependent on module-level state. For instance an AddUser method should not be reliant on private/protected state, it should ultimately be getting parameters to do things like perform actions or modify state. (kept to a minimum)
So let's assume we want to test a method that should call the DataService Create method and Create is expected to have added the item to the DBContext and assigned an ID. The purpose of the unit test is not to assert what EF actually does, but rather what the code under test should do with the results:
[Test]
public void EnsureAddUserCreatesUser()
{
const string userName = "New User";
const int roleId = 4;
const int UserId = 101;
var mockUserDataService = new Mock<IDataService<User>>();
mockUserDataService.Setup(m => m.Create(It.IsAny<User>())).Callback(m => {m.UserId = userId;});
var testController = new UserController(mockUserDataService.Object);
var user = await testController.AddUser(userName, roleId);
Assert.IsNotNull(user);
Assert.AreEqual(userId, user.UserId);
Assert.AreEqual(userName, user.UserName);
Assert.AreEqual(roleId, user.RoleId);
mockUserDataService.Verify(m => m.Create(It.IsAny<User>()), Times.Once);
}
What a test like this does is set up a mock of our repository/data service telling it to expect its input parameter. Since our AddUser method will be creating a new user based on some values, we tell it to expect It.IsAny<User>() which says "expect a user". From there we can have the mock perform some basic actions as if the DbContext added our user successfully such as populating a PK. This example populates the PK using the Callback method which accepts whatever User was passed in then sets our known PK. The value itself does not matter since we aren't actually inserting data, we just want a known value to get back that we can assert that the mock was actually called. Normally the AddUser might also expect to check the resulting data and return something like a success state with an ID. In this example I had the AddUser return the User. Other tests you might want to assert the behaviour if the AddUser attempts to add a duplicate user. In these cases the Moq might Throw an exception or otherwise return a different result.
From there we have returned the User, so we just assert the values are what were expected, including the PK, and that our mocked method was actually called.
Ultimately when it comes to unit testing, the key points are:
Your DataService/Repository serves as the boundary for the tests. (What you mock)
Your tests test business logic above this boundary.
Your tests mock the boundary, capturing all expected calls to that boundary, and either return known state, take action on passed in state, or throw exceptions based on what behaviour you want to test.
Your tests can then assert the mocks to verify that methods that were expected to be called were called, and any methods that were not expected to be called were not called. (Times.None)
I have following Service with method GetAll() and I wrote test testing this method.
public partial class DocumentTypeService : IDocumentTypeService
{
private readonly IRepository<DocumentType> _documentTypeRepository;
private readonly IMediator _mediator;
public DocumentTypeService(IRepository<DocumentType> documentTypeRepository, IMediator mediator)
{
_documentTypeRepository = documentTypeRepository;
_mediator = mediator;
}
public virtual async Task<IList<DocumentType>> GetAll()
{
var query = from t in _documentTypeRepository.Table
orderby t.DisplayOrder
select t;
return await query.ToListAsync();
}
}
This my test method GetAllDocumentTypes():
[TestClass()]
public class DocumentTypeServiceTests
{
private Mock<IRepository<DocumentType>> _documentTypeRepositoryMock;
private DocumentTypeService _documentTypeService;
private Mock<IMediator> _mediatorMock;
private Mock<IMongoQueryable<DocumentType>> _mongoQueryableMock;
private List<DocumentType> _expected;
private IQueryable<DocumentType> _expectedQueryable;
[TestInitialize()]
public void Init()
{
_mediatorMock = new Mock<IMediator>();
_documentTypeRepositoryMock = new Mock<IRepository<DocumentType>>();
_mongoQueryableMock = new Mock<IMongoQueryable<DocumentType>>();
_expected = new List<DocumentType>
{
new DocumentType() {Name = "name1", Description = "t1", DisplayOrder = 0},
new DocumentType() {Name = "name2", Description = "t2", DisplayOrder = 1}
};
_expectedQueryable = _expected.AsQueryable();
_mongoQueryableMock.Setup(x => x.ElementType).Returns(_expectedQueryable.ElementType);
_mongoQueryableMock.Setup(x => x.Expression).Returns(_expectedQueryable.Expression);
_mongoQueryableMock.Setup(x => x.Provider).Returns(_expectedQueryable.Provider);
_mongoQueryableMock.Setup(x => x.GetEnumerator()).Returns(_expectedQueryable.GetEnumerator());
_documentTypeRepositoryMock.Setup(x => x.Table).Returns(_mongoQueryableMock.Object);
_documentTypeService = new DocumentTypeService(_documentTypeRepositoryMock.Object, _mediatorMock.Object);
}
[TestMethod()]
public async Task GetAllDocumentTypes()
{
var actual = await _documentTypeService.GetAll();
Assert.AreEqual(_expected.Count, actual.Count);
}
}
Get error:
Message:
Test method Grand.Services.Tests.Documents.DocumentTypeServiceTests.GetAllDocumentTypes threw exception:
System.InvalidCastException: Unable to cast object of type 'System.Linq.EnumerableQuery`1[Grand.Domain.Documents.DocumentType]' to type 'MongoDB.Driver.Linq.IOrderedMongoQueryable`1[Grand.Domain.Documents.DocumentType]'.
Stack Trace:
MongoQueryable.OrderBy[TSource,TKey](IMongoQueryable`1 source, Expression`1 keySelector)
DocumentTypeService.GetAll() line 38
DocumentTypeServiceTests.GetAllDocumentTypes() line 101
ThreadOperations.ExecuteWithAbortSafety(Action action)
Could you please explain me why type is not IOrderedMongoQueryable and how solve this issue? Thank you
NEW (2020-09-15)
I reproduced your effort as nearly as I could. There are two problems.
First, your GetAll() method includes an orderby, which is what's forcing IMongoQueryable to become IOrderedMongoQueryable. However, _mongoQueryableMock doesn't return IOrderedMongoQueryable. And if you try replacing _mongoQueryableMock's IMongoQueryable with IOrderedMongoQueryable, that fails, too. I didn't find a way to get _expectedQueryable to allow an IOrderedQueryable.
Second, Async is probably going to give you trouble. Without Async, I can change the GetAll() query to something like this, which resolves the query before applying the orderby:
public List<DocumentType> GetAll()
{
var query = from t in _documentTypeRepository.Table.ToList()
orderby t.DisplayOrder
select t;
return query.ToList();
}
However, I didn't find a way to manage the ToListAsync.
All in all, I'm back to my original advice. Don't try to mock IMongoQueryable. Change the service to accept MongoClient. Either way you're mocking Mongo, but accepting IMongoClient you're using it directly instead of hiding it behind another abstraction.
private readonly IMongoClient _mongoClient;
public DocumentTypeService(IMongoClient mongoClient) {...}
ORIGINAL (2020-09-14)
I can't be completely sure, but I believe it's because _documentTypeRepository isn't returning IMongoQueryable. It's returning a Table, which will force an ordered mongo queryable.
For this to work using the mocking method described by me here, _documentTypeRepository would either need to return IMongoQueryable then convert to Table, or see if IQueryable can convert to IOrderedMongoQueryable.
In short, nothing is mocking the return of IOrderedMongoQueryable.
Honestly, what this all points to is that the repository is probably too tightly coupled to the implementation. Forcing IMongoQueryable to IQueryable is a code smell and anti-pattern. You might consider rethinking your service and/or repository layer in one of two ways:
Create an _documentTypeRepository.GetAll() method that maps the MongoDb results to DocumentType. OR
Don't use a repository pattern in the service. Instead, inject the IMongoClient, return the data from Mongo as part of the GetAll method, and map to DocumentType there.
I'm having a difficult time trying to understand how to appropriately return mocked data from a simulated database call in a unit test.
Here's an example method I want to unit test (GetBuildings):
public class BuildingService : IBuildingService {
public IQueryable<Building> GetBuildings(int propertyId)
{
IQueryable<Building> buildings;
// Execution path for potential exception thrown
// if (...) throw new SpecialException();
// Another execution path...
// if (...) ...
using (var context = DataContext.Instance())
{
var Params = new List<SqlParameter>
{
new SqlParameter("#PropertyId", propertyId)
};
// I need to return mocked data here...
buildings = context
.ExecuteQuery<Building>(System.Data.CommandType.StoredProcedure, "dbo.Building_List", Params.ToArray<object>())
.AsQueryable();
}
return buildings;
}
}
So GetBuildings calls a stored procedure.
So I need to mock the DataContext, that of which I can override and set a testable instance. So what happens here is, in the above example DataContext.Instance() does return the mocked object.
[TestFixture]
public class BuildingServiceTests
{
private Mock<IDataContext> _mockDataContext;
[SetUp]
public void Setup() {
_mockDataContext = new Mock<IDataContext>();
}
[TearDown]
public void TearDown() {
...
}
[Test]
public void SomeTestName() {
_mockDataContext.Setup(r =>
r.ExecuteQuery<Building>(CommandType.StoredProcedure, "someSproc"))
.Returns(new List<Building>() { new Building() { BuildingId = 1, Title = "1" }}.AsQueryable());
DataContext.SetTestableInstance(_mockDataContext.Object);
var builings = BuildingService.GetBuildings(1, 1);
// Assert...
}
Please ignore some of the parameters, like propertyId. I've stripped those out and simplified this all. I simply can't get the ExecuteQuery method to return any data.
All other simple peta-poco type methods I can mock without issue (i.e. Get, Insert, Delete).
Update
DataContext.Instance returns the active instance of the DataContext class, if exists, and if not exists, returns a new one. So the method of test under question returns the mocked instance.
Do not mock DataContext. Because mocking DataContext will produce tests tightly coupled to the implementation details of DataContext. And you will be forced to change tests for every change in the code even behavior will remain same.
Instead introduce a "DataService" interface and mock it in the tests for BuildingService.
public interface IDataService
{
IEnumerable<Building> GetBuildings(int propertyId)
}
Then, you can tests implementation of IDataService agains real database as part of integration tests or tests it agains database in memory.
If you can test with "InMemory" database (EF Core or Sqlite) - then even better -> write tests for BuildingService against actual implementation of DataContext.
In tests you should mock only external resources (web service, file system or database) or only resources which makes tests slow.
Not mocking other dependencies will save you time and give freedom while you refactoring your codebase.
After update:
Based on the updated question, where BuildingService have some execution path - you can still testing BuildingService and abstract data related logic to the IDataService.
For example below is BuildingService class
public class BuildingService
{
private readonly IDataService _dataService;
public BuildingService(IDataService dataService)
{
_dataService = dataService;
}
public IEnumerable<Building> GetBuildings(int propertyId)
{
if (propertyId < 0)
{
throw new ArgumentException("Negative id not allowed");
}
if (propertyId == 0)
{
return Enumerable.Empty<Building>();
}
return _myDataService.GetBuildingsOfProperty(int propertyId);
}
}
In tests you will create a mock for IDataService and pass it to the constructor of BuildingService
var fakeDataService = new Mock<IDataContext>();
var serviceUnderTest = new BuildingService(fakeDataService);
Then you will have tests for:
"Should throw exception when property Id is negative"
"Should return empty collection when property Id equals zero"
"Should return collection of expected buildings when valid property Id is given"
For last test case you will mock IDataService to return expected building only when correct propertyId is given to _dataService.GetBuildingsOfProperty method
In order for the mock to return data is needs to be set up to behave as expected given a provided input.
currently in the method under test it is being called like this
buildings = context
.ExecuteQuery<Building>(System.Data.CommandType.StoredProcedure, "dbo.Building_List", Params.ToArray<object>())
.AsQueryable();
Yet in the test the mock context is being setup like
_mockDataContext.Setup(r =>
r.ExecuteQuery<Building>(CommandType.StoredProcedure, "someSproc"))
.Returns(new List<Building>() { new Building() { BuildingId = 1, Title = "1" }}.AsQueryable());
Note what the mock is told to expect as parameters.
The mock will only behave as expected when provided with those parameters. Otherwise it will return null.
Consider the following example of how the test can be exercised based on the code provided in the original question.
[Test]
public void SomeTestName() {
//Arrange
var expected = new List<Building>() { new Building() { BuildingId = 1, Title = "1" }}.AsQueryable();
_mockDataContext
.Setup(_ => _.ExecuteQuery<Building>(CommandType.StoredProcedure, It.IsAny<string>(), It.IsAny<object[]>()))
.Returns(expected);
DataContext.SetTestableInstance(_mockDataContext.Object);
var subject = new BuildingService();
//Act
var actual = subject.GetBuildings(1);
// Assert...
CollectionAssert.AreEquivalent(expected, actual);
}
That said, the current design of the system under test is tightly coupled to a static dependency which is a code smell and makes the current design follow some bad practices.
The static DataContext which is currently being used as a factory should be refactored as such,
public interface IDataContextFactory {
IDataContext CreateInstance();
}
and explicitly injected into dependent classes instead of calling the static factory method
public class BuildingService : IBuildingService {
private readonly IDataContextFactory factory;
public BuildingService(IDataContextFactory factory) {
this.factory = factory
}
public IQueryable<Building> GetBuildings(int propertyId) {
IQueryable<Building> buildings;
using (var context = factory.CreateInstance()) {
var Params = new List<SqlParameter> {
new SqlParameter("#PropertyId", propertyId)
};
buildings = context
.ExecuteQuery<Building>(System.Data.CommandType.StoredProcedure, "dbo.Building_List", Params.ToArray<object>())
.AsQueryable();
}
return buildings;
}
}
This will allow for a proper mock to be created in injected into the subject under test without using a static workaround hack.
[Test]
public void SomeTestName() {
//Arrange
var expected = new List<Building>() { new Building() { BuildingId = 1, Title = "1" }}.AsQueryable();
_mockDataContext
.Setup(_ => _.ExecuteQuery<Building>(CommandType.StoredProcedure, It.IsAny<string>(), It.IsAny<object[]>()))
.Returns(expected);
var factoryMock = new Mock<IDataContextFactory>();
factoryMock
.Setup(_ => _.CreateInstance())
.Returns(_mockDataContext.Object);
var subject = new BuildingService(factoryMock.Object);
//Act
var actual = subject.GetBuildings(1);
// Assert...
CollectionAssert.AreEquivalent(expected, actual);
}
I have a Web API 2.0 project that I am unit testing. My controllers have a Unit of Work. The Unit of Work contains numerous Repositories for various DbSets. I have a Unity container in the Web API and I am using Moq in the test project. Within the various repositories, I use the Find method of Entity Framework to locate an entity based on it's key. Additionally, I am using Entity Framework 6.0.
Here is an very general example of the Unit of Work:
public class UnitOfWork
{
private IUnityContainer _container;
public IUnityContainer Container
{
get
{
return _container ?? UnityConfig.GetConfiguredContainer();
}
}
private ApplicationDbContext _context;
public ApplicationDbContext Context
{
get { _context ?? Container.Resolve<ApplicationDbContext>(); }
}
private GenericRepository<ExampleModel> _exampleModelRepository;
public GenericRepository<ExampleModel> ExampleModelRepository
{
get { _exampleModelRepository ??
Container.Resolve<GenericRepository<ExampleModel>>(); }
}
//Numerous other repositories and some additional methods for saving
}
The problem I am running into is that I use the Find method for some of my LINQ queries in the repositories. Based on this article, MSDN: Testing with your own test doubles (EF6 onwards), I have to create a TestDbSet<ExampleModel> to test the Find method. I was thinking about customizing the code to something like this:
namespace TestingDemo
{
class TestDbSet : TestDbSet<TEntity>
{
public override TEntity Find(params object[] keyValues)
{
var id = (string)keyValues.Single();
return this.SingleOrDefault(b => b.Id == id);
}
}
}
I figured I would have to customize my code so that TEntity is a type of some base class that has an Id property. That's my theory, but I'm not sure this is the best way to handle this.
So I have two questions. Is the approach listed above valid? If not, what would be a better approach for overriding the Find method in the DbSet with the SingleOrDefault method? Also, this approach only really works if their is only one primary key. What if my model has a compound key of different types? I would assume I would have to handle those individually. Okay, that was three questions?
To expand on my comment earlier, I'll start with my proposed solution, and then explain why.
Your problem is this: your repositories have a dependency on DbSet<T>. You are unable to test your repositories effectively because they depend on DbSet<T>.Find(int[]), so you have decided to substitute your own variant of DbSet<T> called TestDbSet<T>. This is unnecessary; DbSet<T> implements IDbSet<T>. Using Moq, we can very cleanly create a stub implementation of this interface that returns a hard coded value.
class MyRepository
{
public MyRepository(IDbSet<MyType> dbSet)
{
this.dbSet = dbSet;
}
MyType FindEntity(int id)
{
return this.dbSet.Find(id);
}
}
By switching the dependency from DbSet<T> to IDbSet<T>, the test now looks like this:
public void MyRepository_FindEntity_ReturnsExpectedEntity()
{
var id = 5;
var expectedEntity = new MyType();
var dbSet = Mock.Of<IDbSet<MyType>>(set => set.Find(It.is<int>(id)) === expectedEntity));
var repository = new MyRepository(dbSet);
var result = repository.FindEntity(id);
Assert.AreSame(expectedEntity, result);
}
There - a clean test that doesn't expose any implementation details or deal with nasty mocking of concrete classes and lets you substitute out your own version of IDbSet<MyType>.
On a side note, if you find yourself testing DbContext - don't. If you have to do that, your DbContext is too far up the stack and it will hurt if you ever try and move away from Entity Framework. Create an interface that exposes the functionality you need from DbContext and use that instead.
Note: I used Moq above. You can use any mocking framework, I just prefer Moq.
If your model has a compound key (or has the capability to have different types of keys), then things get a bit trickier. The way to solve that is to introduce your own interface. This interface should be consumed by your repositories, and the implementation should be an adapter to transform the key from your composite type into something that EF can deal with. You'd probably go with something like this:
interface IGenericDbSet<TKeyType, TObjectType>
{
TObjectType Find(TKeyType keyType);
}
This would then translate under the hood in an implementation to something like:
class GenericDbSet<TKeyType,TObjectType>
{
GenericDbSet(IDbSet<TObjectType> dbset)
{
this.dbset = dbset;
}
TObjectType Find(TKeyType key)
{
// TODO: Convert key into something a regular dbset can understand
return this.dbset(key);
}
}
I realise this is an old question, but after coming up against this issue myself when mocking data for unit tests I wrote this generic version of the 'Find' method that can be used in the TestDBSet implementation that is explained on msdn
Using this method means you dont have to create concrete types for each of your DbSets. One point to note is that this implementaion works if your entities have primary keys in one of the following forms (im sure you could modify to suite other forms easily):
'Id'
'ID'
'id'
classname +'id'
classname +'Id'
classname + 'ID'
public override T Find(params object[] keyValues)
{
ParameterExpression _ParamExp = Expression.Parameter(typeof(T), "a");
Expression _BodyExp = null;
Expression _Prop = null;
Expression _Cons = null;
PropertyInfo[] props = typeof(T).GetProperties();
var typeName = typeof(T).Name.ToLower() + "id";
var key = props.Where(p => (p.Name.ToLower().Equals("id")) || (p.Name.ToLower().Equals(typeName))).Single();
_Prop = Expression.Property(_ParamExp, key.Name);
_Cons = Expression.Constant(keyValues.Single(), key.PropertyType);
_BodyExp = Expression.Equal(_Prop, _Cons);
var _Lamba = Expression.Lambda<Func<T, Boolean>>(_BodyExp, new ParameterExpression[] { _ParamExp });
return this.SingleOrDefault(_Lamba);
}
Also from a performance point of view its not going to be as quick as the recommended method, but for my purposes its fine.
So based on the example, I did the following to be able to Unit Test my UnitOfWork.
Had to make sure my UnitOfWork was implementing IApplicationDbContext. (Also, when I say UnitOfWork, my controller's UnitOfWork is of type IUnitOfWork.)
I left all of the DbSet's in my IApplicationDbContext alone. I chose this pattern once I noticed IDbSet didn't include RemoveRange and FindAsync, which I use throughout my code. Also, with EF6, the DbSet can be set to virtual and this was recommended in MSDN, so that made sense.
I followed the Creating the in-memory test doubles
example to create the TestDbContext and all the recommended classes (e.g. TestDbAsyncQueryProvider, TestDbAsyncEnumerable, TestDbAsyncEnumerator.) Here is the code:
public class TestContext : DbContext, IApplicationDbContext
{
public TestContext()
{
this.ExampleModels= new TestBaseClassDbSet<ExampleModel>();
//New up the rest of the TestBaseClassDbSet that are need for testing
//Created an internal method to load the data
_loadDbSets();
}
public virtual DbSet<ExampleModel> ExampleModels{ get; set; }
//....List of remaining DbSets
//Local property to see if the save method was called
public int SaveChangesCount { get; private set; }
//Override the SaveChanges method for testing
public override int SaveChanges()
{
this.SaveChangesCount++;
return 1;
}
//...Override more of the DbContext methods (e.g. SaveChangesAsync)
private void _loadDbSets()
{
_loadExampleModels();
}
private void _loadExampleModels()
{
//ExpectedGlobals is a static class of the expected models
//that should be returned for some calls (e.g. GetById)
this.ExampleModels.Add(ExpectedGlobal.Expected_ExampleModel);
}
}
As I mentioned in my post, I needed to implement the FindAsync method, so I added a class called TestBaseClassDbSet, which is an alteration of the TestDbSet class in the example. Here is the modification:
//BaseModel is a class that has a key called Id that is of type string
public class TestBaseClassDbSet<TEntity> :
DbSet<TEntity>
, IQueryable, IEnumerable<TEntity>
, IDbAsyncEnumerable<TEntity>
where TEntity : BaseModel
{
//....copied all the code from the TestDbSet class that was provided
//Added the missing functions
public override TEntity Find(params object[] keyValues)
{
var id = (string)keyValues.Single();
return this.SingleOrDefault(b => b.Id == id);
}
public override Task<TEntity> FindAsync(params object[] keyValues)
{
var id = (string)keyValues.Single();
return this.SingleOrDefaultAsync(b => b.Id == id);
}
}
Created an instance of TestContext and passed that into my Mock.
var context = new TestContext();
var userStore = new Mock<IUserStore>();
//ExpectedGlobal contains a static variable call Expected_User
//to be used as to populate the principle
// when mocking the HttpRequestContext
userStore
.Setup(m => m.FindByIdAsync(ExpectedGlobal.Expected_User.Id))
.Returns(Task.FromResult(ExpectedGlobal.Expected_User));
var mockUserManager = new Mock(userStore.Object);
var mockUnitOfWork =
new Mock(mockUserManager.Object, context)
{ CallBase = false };
I then inject the mockUnitOfWork into the controller, and voila. This implementation seems to be working perfect. That said, based on some feeds I have read online, it will probably be scrutinized by some developers, but I hope some others find this to be useful.