How do you mock adding items to a repository or DbContext using moq? - entity-framework

The examples I've seen for using moq for a repository only show how to mock things being returned. I have a somewhat strange requirement: when a query is executed, if a condition exists, a certain item should be added to the repository. I am wondering how to test this without querying the database. I know how to mock the condition existing, but then how do you setup the mock so that you can test that the certain item is added?

Try to use fake in memory repository instead of moq, for example universal generic repository for all entities:
public interface IInMemoryRepository<T> where T : class
{
IQueryable<T> GetAll();
void Create(T item);
void Update(T item);
T GetItem(Expression<Func<T, bool>> expression);
void Delete(T item);
}
public class InMemoryRepository<T> : IInMemoryRepository<T> where T : class
{
private int _incrementer = 0;
public Dictionary<int, T> List = new Dictionary<int, T>();
public IQueryable<T> GetAll()
{
return List.Select(x => x.Value).AsQueryable();
}
public void Create(T item)
{
_incrementer++;
item.GetType().GetProperties().First(p => p.Name == "Id").SetValue(item, _incrementer, null);
List.Add(_incrementer, item);
}
public void Update(T item)
{
var key = (int)item.GetType().GetProperties().First(p => p.Name == "Id").GetValue(item, null);
List[key] = item;
}
public T GetItem(Expression<Func<T, bool>> expression)
{
return List.Select(x => x.Value).SingleOrDefault(expression.Compile());
}
public void Delete(T item)
{
var key = (int)item.GetType().GetProperties().First(p => p.Name == "Id").GetValue(item, null);
List.Remove(key);
}
}

You would not mock the repository; you would have an alternate repository that would use an in-memory store instead of the database, then use IoC to select the correct repository implementation for tests/code.

This blog article might be of use, although my design has changed somewhat since I wrote the post and I really need to update it. I used teh generic repository pattern in a way that enables the DbContext to be mocked. This allows the data access layer to be tested 'right up to the edges'.

Times have changed -- since the release of Entity Framework 6 it has become much easier to mock database context and datasets. This article outlines the particulars.
Testing non-query scenarios
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System.Data.Entity;
namespace TestingDemo
{
[TestClass]
public class NonQueryTests
{
[TestMethod]
public void CreateBlog_saves_a_blog_via_context()
{
var mockSet = new Mock<DbSet<Blog>>();
var mockContext = new Mock<BloggingContext>();
mockContext.Setup(m => m.Blogs).Returns(mockSet.Object);
var service = new BlogService(mockContext.Object);
service.AddBlog("ADO.NET Blog", "http://blogs.msdn.com/adonet");
mockSet.Verify(m => m.Add(It.IsAny<Blog>()), Times.Once());
mockContext.Verify(m => m.SaveChanges(), Times.Once());
}
}
}
Testing query scenarios
Query testing is pretty sweet now, because you can build up test data sets in code and then execute your tests against them:
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
namespace TestingDemo
{
[TestClass]
public class QueryTests
{
[TestMethod]
public void GetAllBlogs_orders_by_name()
{
var data = new List<Blog>
{
new Blog { Name = "BBB" },
new Blog { Name = "ZZZ" },
new Blog { Name = "AAA" },
}.AsQueryable();
var mockSet = new Mock<DbSet<Blog>>();
mockSet.As<IQueryable<Blog>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<Blog>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<Blog>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<Blog>>().Setup(m => m.GetEnumerator()).Returns(0 => data.GetEnumerator());
var mockContext = new Mock<BloggingContext>();
mockContext.Setup(c => c.Blogs).Returns(mockSet.Object);
var service = new BlogService(mockContext.Object);
var blogs = service.GetAllBlogs();
Assert.AreEqual(3, blogs.Count);
Assert.AreEqual("AAA", blogs[0].Name);
Assert.AreEqual("BBB", blogs[1].Name);
Assert.AreEqual("ZZZ", blogs[2].Name);
}
}
}

You can do this by mocking the DbSet.Add() method, like so:
[Fact]
public void CreateBlog_saves_a_blog_via_context()
{
var data = new List<Blog>
{
new Blog { Name = "BBB" },
new Blog { Name = "ZZZ" },
new Blog { Name = "AAA" },
};
var mockSet = new Mock<DbSet<Blog>>();
mockSet.Setup(blogs => blogs.Add(It.IsAny<Blog>)).Returns<Blog>(blog =>
{
data.Add(blog);
return blog;
});
var mockContext = new Mock<BloggingContext>();
mockContext.Setup(m => m.Blogs).Returns(mockSet.Object);
var service = new BlogService(mockContext.Object);
var blog = service.AddBlog("_ADO.NET Blog", "http://blogs.msdn.com/adonet");
var blogs = service.GetAllBlogs();
mockSet.Verify(m => m.Add(It.IsAny<Blog>()), Times.Once());
mockContext.Verify(m => m.SaveChanges(), Times.Once());
Assert.NotNull(blog)
Assert.Equal(4, blogs.Count);
Assert.Equal("AAA", blogs(1).Name);
Assert.Equal("BBB", blogs(2).Name);
Assert.Equal("ZZZ", blogs(3).Name);
}
This is adapted from the documentation found here.

Related

Replace SQLite to LocalDb for unittesting for a ABP.io project

I am working with ABP and I used the basic setup which is generated by the abp cli with EF Core and SQL Server.
Now, I would like to replace SQLite (the generated one), with a LocalDb database, since I using spatial data for unit- and integration testing.
However, I am not sure how to make the replacement. I have identified the file which initialize the SQLite, and it is reseeded after each unittest.
When switching to LocalDb, I will not flush / seed database, and I think I have solved that out.
This is the generated file ...\ABPProject\aspnet-core\ABPProject.EntityFrameworkCore.Tests\EntityFrameworkCore\ABPProjectEntityFrameworkCoreTestModule.cs that uses SQLite, can you please give me an example / ideas how to use LocalDb instead of SQLite?
[DependsOn(
typeof(ABPProjectEntityFrameworkCoreModule),
typeof(ABPProjectTestBaseModule),
typeof(AbpEntityFrameworkCoreSqliteModule)
)]
public class ABPProjectEntityFrameworkCoreTestModule : AbpModule
{
private SqliteConnection _sqliteConnection;
public override void ConfigureServices(ServiceConfigurationContext context)
{
ConfigureInMemorySqlite(context.Services);
}
private void ConfigureInMemorySqlite(IServiceCollection services)
{
_sqliteConnection = CreateDatabaseAndGetConnection();
services.Configure<AbpDbContextOptions>(options =>
{
options.Configure(context =>
{
context.DbContextOptions.UseSqlite(_sqliteConnection);
});
});
}
public override void OnApplicationShutdown(ApplicationShutdownContext context)
{
_sqliteConnection.Dispose();
}
private static SqliteConnection CreateDatabaseAndGetConnection()
{
var connection = new SqliteConnection("Data Source=:memory:");
connection.Open();
var options = new DbContextOptionsBuilder<ABPProjectDbContext>()
.UseSqlite(connection)
.Options;
using (var context = new ABPProjectDbContext(options))
{
context.GetService<IRelationalDatabaseCreator>().CreateTables();
}
return connection;
}
}
This was really simple. Just do this:
namespace ABPProject.EntityFrameworkCore;
[DependsOn(
typeof(ABPProjectEntityFrameworkCoreModule),
typeof(ABPProjectTestBaseModule),
typeof(AbpEntityFrameworkCoreSqliteModule)
)]
public class ABPProjectEntityFrameworkCoreTestModule : AbpModule
{
private SqliteConnection _sqliteConnection;
public override void ConfigureServices(ServiceConfigurationContext context)
{
//ConfigureInMemorySqlite(context.Services);
ConfigureLocalDb(context.Services);
}
private void ConfigureInMemorySqlite(IServiceCollection services)
{
_sqliteConnection = CreateDatabaseAndGetConnection_Sqlite();
services.Configure<AbpDbContextOptions>(options =>
{
options.Configure(context =>
{
context.DbContextOptions.UseSqlite(_sqliteConnection);
});
});
}
private void ConfigureLocalDb(IServiceCollection services)
{
services.Configure<AbpDbContextOptions>(options =>
{
options.Configure(context =>
{
context.DbContextOptions.UseSqlServer("Server=(LocalDb)\\MSSQLLocalDB;Database=ABPProject_Unittest;Trusted_Connection=True;TrustServerCertificate=True");
});
});
}
public override void OnApplicationShutdown(ApplicationShutdownContext context)
{
if (_sqliteConnection is not null)
{
_sqliteConnection.Dispose();
}
}
private static SqliteConnection CreateDatabaseAndGetConnection_Sqlite()
{
var connection = new SqliteConnection("Data Source=:memory:");
connection.Open();
var options = new DbContextOptionsBuilder<ABPProjectDbContext>()
.UseSqlite(connection, x => x.UseNetTopologySuite())
.Options;
using (var context = new ABPProjectDbContext(options))
{
context.GetService<IRelationalDatabaseCreator>().CreateTables();
}
return connection;
}
}
Then you need to disable re-seeding each time between test in aspnet-core\test\ABPProject.TestBase\ABPProjectTestBaseModule.cs
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
//SeedTestData(context);
}
How to reset the database when starting all tests? Use XUnit collections, which enable setup before all tests are starting and teardown after all tests are executed.
You can read more about it here https://xunit.net/docs/shared-context but basically, you create a new class (NOTE! You need to place this file in each test project, since the class can not be shared between different test projects):
public class DatabaseFixture : ABPProjectTestBase<ABPProjectEntityFrameworkCoreTestModule>, IAsyncLifetime
{
// ... create your shared vars here
public DatabaseFixture()
{
}
public async Task InitializeAsync()
{
IDbContextProvider<ABPProjectDbContext> _dbContextProvider = GetRequiredService<IDbContextProvider<ABPProjectDbContext>>();
IUnitOfWorkManager _unitOfWorkManager = GetRequiredService<IUnitOfWorkManager>();
IDataSeeder _dataSeeder = GetRequiredService<IDataSeeder>();
using (var uow = _unitOfWorkManager.Begin())
{
var context = await _dbContextProvider.GetDbContextAsync();
var tableNames = context.Model.GetEntityTypes()
.Where(t => t.GetTableName().StartsWith("App"))
.Select(t => t.GetTableName())
.Distinct()
.ToList();
// Disable constraints
foreach (var tableName in tableNames)
{
await context.Database.ExecuteSqlRawAsync($"ALTER TABLE {tableName} NOCHECK CONSTRAINT ALL");
}
// Remove rows
foreach (var tableName in tableNames)
{
await context.Database.ExecuteSqlRawAsync($"DELETE FROM {tableName}");
}
// Re-enable constraints
foreach (var tableName in tableNames)
{
await context.Database.ExecuteSqlRawAsync($"ALTER TABLE {tableName} CHECK CONSTRAINT ALL");
}
}
// Re - seed database
await _dataSeeder.SeedAsync();
}
public async Task DisposeAsync()
{
// ... clean up test data from the database with and use async calls here...
}
}
[CollectionDefinition("Database collection")]
public class DatabaseCollection : ICollectionFixture<DatabaseFixture>
{
// This class has no code, and is never created. Its purpose is simply
// to be the place to apply [CollectionDefinition] and all the
// ICollectionFixture<> interfaces.
}
The last thing is to decorate each test class with following:
namespace ABPProject.Things
{
[Collection("Database collection")]
public class ThingAppService_Tests : ABPProjectApplicationTestBase
{
public readonly DatabaseFixture _fixture;
private readonly IThingAppService _thingAppService;
public ThingAppService_Tests(DatabaseFixture fixture)
{
_fixture = fixture;
_thingAppService = GetRequiredService<IThingAppService>();
}
[Fact]
public async Task GetListAsync_Should_Get_all_Things()
{
// This is a test
}
}
}

Create unit test for View() with a List in ASP.net core MVC application

I am new in unit testing. I have a controller - StudentsController with dependency injection and there my Index() method:
public class StudentsController : Controller
{
public readonly UniversityContext _context;//Database
public StudentsController(UniversityContext context)
{
_context = context;
}//Constructor with database
// GET: Students
public async Task<IActionResult> Index()
{
return View(await _context.Students.ToListAsync());
}
}
Next i need to write a correct unit test code, that check, if:
1) a View() have a list with my students
2) The query with students is not null.
I read about Mock objects, but I don't know how to write the correct code. My code that I wrote so far:
public class StudentsControllerTests
{
[Fact]
public async Task Index_ReturnsAViewResult_WithAListOfStudents()
{
var mockRepo = new Mock<UniversityContext>();
mockRepo.Setup(repo => repo.Students.ToList()).Returns(GetTestStudents());//There i get following error:Expression references a method that does not belong to the mocked object
var controller = new StudentsController(mockRepo.Object);
// Act
var result = controller.Index();
//// Assert
var viewResult = Assert.IsType<ViewResult>(result);
var model = Assert.IsAssignableFrom<IEnumerable<Student>>(
viewResult.ViewData.Model);
Assert.NotNull(model);//Second Condition
}
public List<Student> GetTestStudents()
{
var sessions = new List<Student>();
sessions.Add(new Student()
{
bDate = new DateTime(1994, 7, 2),
Name = "Test One"
});
sessions.Add(new Student()
{
bDate = new DateTime(1995, 7, 1),
Name = "Test Two"
});
return sessions;
}
}
Can someone explain me, how to correct my code?
You only need to mock the members of the context, which in this case is the .Students property. ToList is an extension method being call on the property and cannot be mocked by moq.
Also .Students is a DbSet and would need to be mocked as well.
Using the test classes from this answer :
How to mock an async repository with Entity Framework Core
The following generic extension methods were derived
public static class MockDbSetExtensions {
public static Mock<DbSet<T>> AsDbSetMock<T>(this IEnumerable<T> list) where T : class {
IQueryable<T> queryableList = list.AsQueryable();
Mock<DbSet<T>> dbSetMock = new Mock<DbSet<T>>();
dbSetMock.As<IQueryable<T>>().Setup(x => x.Provider).Returns(queryableList.Provider);
dbSetMock.As<IQueryable<T>>().Setup(x => x.Expression).Returns(queryableList.Expression);
dbSetMock.As<IQueryable<T>>().Setup(x => x.ElementType).Returns(queryableList.ElementType);
dbSetMock.As<IQueryable<T>>().Setup(x => x.GetEnumerator()).Returns(() => queryableList.GetEnumerator());
return dbSetMock;
}
public static Mock<DbSet<T>> ToAsyncDbSetMock<T>(this IEnumerable<T> source)
where T : class {
var data = source.AsQueryable();
var mockSet = new Mock<DbSet<T>>();
mockSet.As<IAsyncEnumerable<T>>()
.Setup(m => m.GetEnumerator())
.Returns(new TestAsyncEnumerator<T>(data.GetEnumerator()));
mockSet.As<IQueryable<T>>()
.Setup(m => m.Provider)
.Returns(new TestAsyncQueryProvider<T>(data.Provider));
mockSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(() => data.GetEnumerator());
return mockSet;
}
}
With the above utilities, update
mockRepo.Setup(repo => repo.Students.ToList()).Returns(GetTestStudents());
To
var studentsMockedDbSet = GetTestStudents().ToAsyncDbSetMock();
mockRepo.Setup(repo => repo.Students).Returns(studentsMockedDbSet.Object);

Entity Framework 6 dbContext not saving changes

In our MVC 5 project our database context is instantiate in the AccountController like this
private CustomersContext _customersContext;
public CustomersContext CustContext
{
get
{
return _customersContext ?? new CustomersContext();
}
private set
{
_customersContext = value;
}
}
Each customer is referred by a number of sources. The routine below changes the UserId of the referral source to a new user.
var referralList = CustContext.Referrals.Where(d => d.UserId == membershipUser.Id);
foreach (Referral referral in referralList)
{
referral.UserId = newUser.Id;
}
Stepping trough the code I can see referral.UserId being updated. However
var result = await CustContext.SaveChangesAsync();
returns 0. The database is not updated.
CustomersContext looks like this
{
public partial class CustomersContext : IdentityDbContext<ApplicationUser>//, ICustomersContext
{
public CustomersContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static CustomersContext Create()
{
return new CustomersContext();
}
public virtual DbSet<ReferralSource> ReferralSources { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ApplicationUser>()
.HasMany(e => e.Referrals)
.WithRequired(e => e.User)
.HasForeignKey(e => e.UserId)
.WillCascadeOnDelete(false);
I don't see any sql emitted in SQL Profiler. Why doesn't the database context save changes?
Before calling var result = await CustContext.SaveChangesAsync(); you need to set the state of the entities that you want to be modified. Somthing like:
var referralList = CustContext.Referrals.Where(d => d.UserId == membershipUser.Id);
foreach (Referral referral in referralList)
{
referral.UserId = newUser.Id;
CustContext.Entry(referral).State = System.Data.Entity.EntityState.Modified;
}
var result = await CustContext.SaveChangesAsync();
The answer provided by #Issac did not solve my problem, but it did put me on the road to a solution. The error
An entity object cannot be referenced by multiple instances of IEntityChangeTracker.
suggested there were multiple instances of dbContext. I removed the context from the CTOR and instantiated the context within a using statement
using (CustomersContext customersContext = new CustomersContext())
{
var referralList = customersContext.Referrals.Where(d => d.UserId == membershipUser.Id);
foreach (Referral referral in referralList)
{
referral.UserId = newUser.Id;
}
var result = await customersContext.SaveChangesAsync();
}
and now all is tickety-boo

Exclude certain entities from second-level caching

I'm using EFCache to provide 2nd-level caching in my EF context.
I ran into a problem, where one of my entities is connected to a view which provides row-level security. So this view filters rows based on some parameters. When using 2nd-level cache, all users will get the same result!
I'm looking for a way to exclude certain entities from caching, any help is welcome.
This is my caching configuration:
class CacheConfiguration : DbConfiguration
{
public CacheConfiguration()
{
var transactionHandler = new CacheTransactionHandler(new InMemoryCache());
AddInterceptor(transactionHandler);
var cachingpolicy = new cachingpolicy();
Loaded +=
(sender, e) => e.ReplaceService<DbProviderServices>(
(s, _) => new CachingProviderServices(s, transactionHandler, cachingPolicy));
}
}
I found the answer in this blog post.
In order to exclude some entities, you need to create a caching policy and drive a class from CachingPolicy.
After overriding CanBeCached method, you can return false to prevent caching.
This is my working code:
class CacheConfiguration : DbConfiguration
{
public CacheConfiguration()
{
var transactionHandler = new CacheTransactionHandler(new InMemoryCache());
AddInterceptor(transactionHandler);
//var cachingPolicy = new CachingPolicy();
var cachingPolicy = new myCachingPolicy();
Loaded +=
(sender, e) => e.ReplaceService<DbProviderServices>(
(s, _) => new CachingProviderServices(s, transactionHandler, cachingPolicy));
}
}
public class myCachingPolicy : CachingPolicy
{
protected override bool CanBeCached(System.Collections.ObjectModel.ReadOnlyCollection<System.Data.Entity.Core.Metadata.Edm.EntitySetBase> affectedEntitySets, string sql, IEnumerable<KeyValuePair<string, object>> parameters)
{
string[] excludedEntities = {
"permView1",
"permView2",
"permView3"};
if (affectedEntitySets.Where(x => excludedEntities.Contains(x.Table)).Any())
{
return false;
}
else
{
return base.CanBeCached(affectedEntitySets, sql, parameters);
}
}
}

How to access repository from IRouter

I'm developing modular application and I'd like for entities from different modules to be able to register their own friendly url slugs.
app.UseMvc(routes =>
{
routes.Routes.Add(new SlugRouter(routes.DefaultHandler));
(...)
});
But following code throws Cannot access a disposed object. Object name: 'CommerceDbContext'. when trying to access slug from the repository.
public class SlugRouter : IRouter
{
private readonly IRouter _target;
public SlugRouter(IRouter target)
{
_target = target;
}
public async Task RouteAsync(RouteContext context)
{
var slugRepository = context.HttpContext.RequestServices.GetService<IRepository<SlugEntity>>();
// ERROR: Cannot access a disposed object. Object name: 'CommerceDbContext'
var urlSlug = await slugRepository.GetAllIncluding(x => x.EntityType).FirstOrDefaultAsync(x => x.Slug == context.HttpContext.Request.Path.Value);
(...)
}
It must be something simple I'm missing to be able to access the repository from router. Thanks for any help.
Begin a unit of work:
public async Task RouteAsync(RouteContext context)
{
var slugRepository = context.HttpContext.RequestServices.GetService<IRepository<SlugEntity>>();
var unitOfWorkManager = context.HttpContext.RequestServices.GetService<IUnitOfWorkManager>();
using (var uow = unitOfWorkManager.Begin())
{
var urlSlug = await slugRepository.GetAllIncluding(x => x.EntityType).FirstOrDefaultAsync(x => x.Slug == context.HttpContext.Request.Path.Value);
await uow.CompleteAsync();
}
}
Access IModel. You do not need dbContext for.
for entities from different modules to be able to register their own
friendly url slugs
I do it this way:
1) move OnModelCreating to static methiod
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
BuildModel(modelBuilder);
}
public static void BuildModel(ModelBuilder modelBuilder)
{
// ...
}
2) Create model where you need:
var conventionSet = new ConventionSet();
var modelBuilder = new ModelBuilder(conventionSet);
AdminkaDbContext.BuildModel(modelBuilder);
var mutableModel = modelBuilder.Model;
There is your meta (in mutableModel ). You can loop through entities (types of entities).