EF 6 fake db context, can't find the entity - c#

I'm in the middle of covering some of our service classes with unit tests and I have managed to isolate/fake the dbcontext using NSubstitute (following this guide). I have some tests done and working, and things seemed to be alright, but now I can't find an entity I added to the context.
The test code is pretty straightforward:
[Fact]
public void CreateStore_GivenAccount_AccountIsAssignedTheStore()
{
const int accountId = 10;
var account = new Account {Id = accountId};
var fakeContext = new FakeContextBuilder()
.WithAccounts(account)
.Build();
var service = new Service(fakeContext);
const int someProperty = 0;
const string someOtherProperty = "blabla";
service.CreateStore(accountId, someProperty, someOtherProperty);
var storeWasAdded = account.Stores
.Any(store =>
store.SomeProperty == someProperty &&
store.SomeOtherProperty == someOtherProperty);
Assert.True(storeWasAdded);
}
The FakeContextBuilder is a helper class I made for setting up the context (similar methods for other entities):
public class FakeContextBuilder
{
private DbSet<Account> _accountTable;
private static DbSet<TEntity> SetUpFakeTable<TEntity>(params TEntity[] entities) where TEntity : class
{
var fakeTable = Substitute.For<DbSet<TEntity>, IQueryable<TEntity>>() as IQueryable<TEntity>;
var table = entities.AsQueryable();
fakeTable.Provider.Returns(table.Provider);
fakeTable.Expression.Returns(table.Expression);
fakeTable.ElementType.Returns(table.ElementType);
fakeTable.GetEnumerator().Returns(table.GetEnumerator());
return (DbSet<TEntity>) fakeTable;
}
public Context Build()
{
var context = Substitute.For<Context>();
context.Accounts.Returns(_accountTable);
return context;
}
public FakeContextBuilder WithAccounts(params Account[] accounts)
{
_accountTable = SetUpFakeTable(accounts);
return this;
}
}
Service method:
public void CreateStore(int accountID, int someProperty, string someOtherProperty)
{
var account = _context.Accounts.Find(accountID);
account.Stores.Add(new Store(someProperty, someOtherProperty));
}
On the Accounts.Find() row I get null instead of the expected account instance. If I add a breakpoint and look at the context I see that "enumerate results" on Accounts yields no results, but I can see that the provider and enumerator etc are set correctly in non-public members. The fake context builder also works fine in other tests, so my guess is that this is related to the Find() method.
EDIT: I have now confirmed that the Find() method is the culprit since the test passes when doing this instead:
var account = _context.Accounts.Single(act => act.Id == 10);
I still want to use Find() for caching purposes and so on. Can this be configured in the test code somehow? Would hate to mess up the production code for this, since it's really a simple operation.

I have solved the problem. It might not be the most neat solution ever, but it seems to do the trick, and I can't see (at the moment at least) that it would be a maintenance nuisance later on.
I pulled it off by creating a sub-class of DbSet<T> which I imaginatively enough named DbSetWithFind<T>
public class DbSetWithFind<TEntity> : DbSet<TEntity> where TEntity : class
{
private readonly IQueryable<TEntity> _dataSource;
public DbSetWithFind(IQueryable<TEntity> dataSource)
{
_dataSource = dataSource;
}
public sealed override TEntity Find(params object[] keyValues) // sealed override prevents EF from "ruining" it.
{
var keyProperties = typeof (TEntity).GetProperties()
.Where(property => property.IsDefined(typeof (KeyAttribute), true));
return _dataSource.SingleOrDefault(entity =>
keyProperties
.Select(property => property.GetValue(entity))
.Intersect(keyValues)
.Any());
}
}
Then I just modified the Substitute.For() call to use the sub-class, containing my custom implementation of Find().
private static DbSet<TEntity> SetUpFakeTable<TEntity>(params TEntity[] entities) where TEntity : class
{
var dataSource = entities.AsQueryable();
var fakeDbSet = Substitute.For<DbSetWithFind<TEntity>, IQueryable<TEntity>>(dataSource); // changed type and added constructor params
var fakeTable = (IQueryable<TEntity>) fakeDbSet;
fakeTable.Provider.Returns(dataSource.Provider);
fakeTable.Expression.Returns(dataSource.Expression);
fakeTable.ElementType.Returns(dataSource.ElementType);
fakeTable.GetEnumerator().Returns(dataSource.GetEnumerator());
return (DbSet<TEntity>) fakeTable;
}

Related

How to resolve scoped service inside singleton object

I have MemoryCache objects (Application,Configuration etc) which I registered them as Singleton. Also there are scoped repositories which selects data from db to fill cache.
For example here is the Singleton registered class,
public class ApplicationCache : MultipleLoadCache<Application>
{
public ApplicationCache()
{
}
}
MultipleLoadCache overrides the CacheItemPolicy, (there is also SingleLoadCache),
public class MultipleLoadCache<TEntity> : SmartCache<TEntity> where TEntity : class
{
public MultipleLoadCache()
{
}
protected override CacheItemPolicy SetPolicy()
{
return new CacheItemPolicy()
{
AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(15)
};
}
}
And base class is,
public class SmartCache<TEntity> : IDisposable where TEntity : class
{
public bool TryGetList(IRepository<TEntity> repository, out List<TEntity> valueList)
{
valueList = null;
lock (cacheLock)
{
GenerateCacheIfNotExists(repository, out valueList);
if (valueList == null || valueList.Count == 0)
{
valueList = (List<TEntity>)_memoryCache.Get(key);
}
}
return valueList != null;
}
I know that scoped services can't be injected to singleton class. So I prefer to use method injection.
private void GenerateCacheIfNotExists(IRepository<TEntity> repository, out List<TEntity> list)
{
list = null;
if (!_memoryCache.Any(x => x.Key == key)) // if key not exists, get db records from repo.
{
IEnumerable<TEntity> tempList = repository.GetList();
list = tempList.ToList();
_cacheItemPolicy = SetPolicy();
SetCacheList(list);
}
}
}
And at controller I try to get cache values, but this part seems wrong to me. If I try to get cache values, I shouldn't pass repository as parameter.
private readonly ApplicationCache _appCache;
public LogController(ApplicationCache appCache)
{
_appCache = appCache;
}
[HttpPost]
[Route("Register")]
public List<Application> Register([FromServices] IApplicationRepository repository)
{
List<Application> cf;
_appCache.TryGetList(repository, out cf);
return cf;
}
Also, by doing Method Injection. I am also unable to use RemovedCallBack event of CacheItemPolicy. Because, when callback triggers (reload cache), I need repository to get records from db again.
Is this design seems nice, what is the best design to do this by using callback events of MemoryCache?
Update 1-
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddMemoryCache();
services.AddSingleton(x => new ApplicationCache());
services.AddScoped<IApplicationRepository, ApplicationRepository>();
}
Thanks,
I had the same issue. Since static classes is compiled at the beginning it cannot inject the required services later. I figured it out by using IServiceScopeFactory.
You basically inject IServiceScopeFactory serviceScopeFactory in the constructer .
static SampleClass(IServiceScopeFactory serviceScopeFactory){
//serviceScopedFactory will act as Singleton, since it is a static class
_serviceScopeFactory = serviceScopeFactory;
}
And use it like this in the method :
using (var scope = _serviceScopeFactory.CreateScope())
{
var service = scope.ServiceProvider.GetRequiredService<IService>();
//Here you can use the service. This will be used as Scoped since it will be
//recreated everytime it is called
}

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.

Microsoft.Azure.Documents.Client CreateDocumentQuery Moq

I am trying to create a moq for Microsoft.Azure.Documents.Client CreateDocumentQuery
I created an interface with class implementation where I instantiate an object of DocumentClient and make corresponding calls. This is to help moq easy for this.
Here are the code:
public interface IASDocumentClient
{
IOrderedQueryable<T> CreateDocumentQuery<T>(Uri documentCollectionUri, FeedOptions feedOptions = null);
}
public class ASDocumentClient : IASDocumentClient
{
DocumentClient client = null;
public ASDocumentClient(DocumentClient documentClient)
{
this.client = documentClient;
}
public IOrderedQueryable<Document> CreateDocumentQuery(Uri documentCollectionOrDatabaseUri, FeedOptions feedOptions = null)
{
return client.CreateDocumentQuery(documentCollectionOrDatabaseUri, feedOptions);
}
public IQueryable<T> CreateDocumentQuery<T>(Uri documentCollectionOrDatabaseUri, SqlQuerySpec querySpec, FeedOptions feedOptions = null)
{
return client.CreateDocumentQuery<T>(documentCollectionOrDatabaseUri, querySpec, feedOptions);
}
}
public class DocumentDBRepositoryUnitTest : IDisposable
{
IDocumentDBRepository<TestDocumentDBEntity> documentDBRepository;
List<TestDocumentDBEntity> items = new List<TestDocumentDBEntity>();
//Pre-test
public DocumentDBRepositoryUnitTest()
{
Mock<IASDocumentClient> documentClient = new Mock<IASDocumentClient>();
documentClient.Setup(x => x.CreateDocumentQuery<Document>(It.IsAny<Uri>(), It.IsAny<FeedOptions>())).Returns(queryDocuments);
}
//Not working now
[Fact]
public void GetItemsAsyncTest()
{
//Arrange
//Act
var retTask = documentDBRepository.GetItemsAsync(x => true);
//Assert
Assert.NotNull(retTask);
Assert.NotNull(retTask.Result);
}
//Post-test
public void Dispose()
{
items = new List<TestDocumentDBEntity>();
}
}
public class DocumentDBRepository<T> : IDocumentDBRepository<T> where T : BaseDocumentDBEntity, new()
{
private string cosmosDbUri;
private string cosmosDbAuthKey;
private string databaseId;
private string collectionId;
private IASDocumentClient client=null;
public DocumentDBRepository(IASDocumentClient client, string databaseId, string collectionId)
{
this.client = client;
this.databaseId = databaseId;
this.collectionId = collectionId;
if (!string.IsNullOrEmpty(this.databaseId) && !string.IsNullOrEmpty(this.collectionId))
Initialize();
}
public async Task<IEnumerable<T>> GetItemsAsync(Expression<Func<T, bool>> predicate)
{
IDocumentQuery<T> query = client.CreateDocumentQuery<T>(
UriFactory.CreateDocumentCollectionUri(this.databaseId, this.collectionId), new FeedOptions { MaxItemCount = -1 })
.Where(predicate)
.AsDocumentQuery();
List<T> results = new List<T>();
while (query.HasMoreResults)
{
results.AddRange(await query.ExecuteNextAsync<T>());
}
return results;
}
}
When I run the test, its not even reaching out to the mock Setup for CreateDocumentQuery:
documentClient.Setup(x => x.CreateDocumentQuery(It.IsAny(), It.IsAny())).Returns(queryDocuments);
Any idea?
The method you are trying to mock is non-virtual. You can only mock abstract methods on a class or mock an interface of a class.
https://github.com/Moq/moq4/wiki/Quickstart - you can see here that IFoo is an interface and Bar and Baz are concrete classes with abstract methods.
I would suggest you create an abstraction on top of this code. The repository pattern looks to be suitable in this case as you seem to be persisting data. This will allow you to mock the repository you create wherever it will be used e.g. in some kind of service layer. As for testing the actual implementation (the bit where you use the Azure SDK) I would suggest writing an integration test that includes this dependency actually saving the data, either to Storage Emulator or a storage account in Azure.
ASDocumentClient does nothing its a 1: 1 wrapper around client so test has negative value ( 0 value + the cost of having the test). You should also not unit test DocumentDB unless you find a specific issue that's Microsofts job ( that can be picked up with integration tests)
IDocumentDBRepository should be tested and can be via your wrapper interface.
Not having ASDocumentClient / IASDocumentClient wrappers to test Repository is a different but useful question.

EF repository created from mock db context returns the dbset only once

I am writing a unit test for my controller.
The test uses an actual Repository class that uses a mock dbcontext inside. I am creating the dbset as in this helper article, and it works all fine - except that it only works one time per unit test.
http://codethug.com/2015/03/20/mocking-dbset/
This means that:
IQueryable<User> membersQuery = this.unitOfWork.UserRepository.Get();
List<User> members = membersQuery.ToList(); //here I get 6 members
membersQuery = this.unitOfWork.UserRepository.Get();
members = membersQuery.ToList(); //when its called again, I get 0 members
Why does it behave like that, I would expect that it shoud return the same collection of members both times.
Here is how I create the repository
var enumerable = new List<User>();
// ... populate the mocks list
var mockedContext = new Mock<MyDbContext>();
mockedContext.Setup(c => c.Users).ReturnsDbSet(enumerable);
mockedContext.Setup(c => c.Set<User>()).ReturnsDbSet(enumerable);
var repo = new Mock<IRepository<User>>();
return new Repository<User>(mockedContext.Object, this.logger);
For clarity, some most important bits of the repository code:
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected internal MyDbContext Context;
protected internal DbSet<TEntity> DbSet;
public Repository(MyDbContext context)
{
this.Context = context;
this.DbSet = context.Set<TEntity>();
}
public virtual IQueryable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null)
{
IQueryable<TEntity> query = this.DbSet;
if (filter != null)
{
query = query.Where(filter);
}
return query;
}
Any idea why does it work like that?
Based on the linked example. The described problem experienced is usually because of this line.
mockSet.As<IQueryable<T>>()
.Setup(m => m.GetEnumerator())
.Returns(queryableData.GetEnumerator()); //<-- this here
return queryableData.GetEnumerator() here alony allows for a one time forward only enumeration.
To allow for multiple enumerations return a function.
mockSet.As<IQueryable<T>>()
.Setup(m => m.GetEnumerator())
.Returns(() => queryableData.GetEnumerator()); //<-- note the change () =>
the function will be called every time an enumeration is needed, allowing for multiple enumerations of the collection.

Unit testing my custom membership provider

I have a custom membership provider which connects to a user repository like this:
public class MyMembershipProvider : MembershipProvider {
[Inject]
public IUserRepository UserRepository { get; set; }
...
//Required membership methods
}
I am using ninject for my DI. Now I would like to test the provider, and have a mock user repository injected to allow me to do this. So something like:
...
IList<User> users = new List<User> {
new User { Email="matt#test.com",
UserName="matt#test.com",
Password="test"
}
};
var mock = new Mock<IUserRepository>();
mock.Setup(mr => mr.FindByUsername(
It.IsAny<string>())).Returns((string s) => users.Where(
x => x.UserName.Equals(s,StringComparison.OrdinalIgnoreCase)).Single());
...
And here is where I am not certain how to proceed, how do I get my mocked repository injected into my provider so that when a unit test that makes calls to the provider uses this mock repository?
Am I asking the right questions here?
EDIT - My final solution
For what it is worth I moved away from using mock to using an InMemory repository to maintain state so the provider would properly test certain functions. Right now I am only using this to test things like my provider. I ended up with:
generic InMemoryRepository:
class InMemoryRepository<TEntity> : IRepository<TEntity> where TEntity : class {
private int _incrementer = 0;
public Dictionary<int, TEntity> List = new Dictionary<int, TEntity>();
public string IDPropertyName {get; set;}
public void Create(TEntity entity) {
_incrementer++;
entity.GetType().GetProperties().First(p => p.Name == IDPropertyName).SetValue(entity, _incrementer, null);
List.Add(_incrementer,entity);
}
public TEntity GetById(int id) {
return List[id];
}
public void Delete(TEntity entity) {
var key = (int)entity.GetType().GetProperties().First(p => p.Name == IDPropertyName).GetValue(entity, null);
List.Remove(key);
}
public void Update(TEntity entity) {
var key = (int)entity.GetType().GetProperties().First(p => p.Name == IDPropertyName).GetValue(entity, null);
List[key] = entity;
}
}
Then my actual user repository - I do not have generic ID fields which is why I am using the IDPropertyName variable:
class InMemoryUserRepository : InMemoryRepository<User>, IUserRepository {
public InMemoryUserRepository() {
this.IDPropertyName = "UserID";
}
public IQueryable<User> Users {
get { return List.Select(x => x.Value).AsQueryable(); }
}
public User FindByUsername(string username) {
int key = List.SingleOrDefault(x=>x.Value.UserName.Equals(username, StringComparison.OrdinalIgnoreCase)).Key;
return List[key];
}
}
My membership test base class:
[TestClass]
public class MyMembershipProviderTestsBaseClass : IntegrationTestsBase {
protected MyMembershipProvider _provider;
protected NameValueCollection _config;
protected MembershipCreateStatus _status = new MembershipCreateStatus();
[TestInitialize]
public override void Initialize() {
base.Initialize();
// setup the membership provider
_provider = new MyMembershipProvider();
MembershipSection section = (MembershipSection) ConfigurationManager.GetSection("system.web/membership");
NameValueCollection collection = section.Providers["MyMembershipProvider"].Parameters;
_provider.Initialize(null, collection);
_status = new MembershipCreateStatus();
}
[TestCleanup]
public override void TestCleanup() {
base.TestCleanup();
}
}
Then my test:
[TestMethod]
public void Membership_CreateUser() {
_provider.UserRepository = new InMemoryUserRepository();
_provider.CreateUser(_email, out _status);
Assert.AreEqual(MembershipCreateStatus.Success, _status);
}
This answer provided inspiration: https://stackoverflow.com/a/13073558/1803682
Since you've exposed your repository as a property, just create an instance of your provider in your test class and set that property to your mock like so:
public void Test()
{
MyMembershipProvider provider = new MyMembershipProvder();
provider.UserRepository = mock.Object;
// Do test stuff here
// Verify mock conditions
}
Presumably your repository implementation is using the UserRepository property so when you test it, this code will use the mocked dependency.
You can setup a test module for Ninject and create the Ninject Kernel using the test module within the unit test project.
This is what an inversion of control container is for. You have one set of bindings configured for running as a website, another set for running in test, another set for running using a different backend (or whatever).
I do this so that the production and test code get initialized in the same fashion (via Ninject) and all that changes is the configuration of Ninject.
Or do what #chris house suggested. That will work too.

Categories