I have a project that uses EF Core, and I'm trying to run unit tests. At the moment they fail when running 'all tests' since apparently the database is not properly reset between tests.
Typically the first test in the list succeeds
Other tests fail with errors such as:
unique key constraint fail (when seeding data... data already exists)
more rows are 'created' than expected for a test (because other rows from other tests are still there)
When I run the tests one by one by hand they all succeed.
I'm using this code to created the context used in the tests:
public class SampleDbContextFactory : IDisposable
{
private DbConnection _connection;
private DbContextOptions<SampleDbContext> CreateOptions()
{
return new DbContextOptionsBuilder<SampleDbContext>()
.UseSqlite(_connection).Options;
}
public SampleDbContext CreateContext()
{
if (_connection == null)
{
_connection = new SqliteConnection("DataSource=:memory:");
_connection.Open();
var options = CreateOptions();
using (var context = new SampleDbContext(options))
{
context.Database.EnsureCreated();
}
}
return new SampleDbContext(CreateOptions());
}
public void Dispose()
{
if (_connection != null)
{
_connection.Dispose();
_connection = null;
}
}
}
Within a test, I call it like this:
using (var factory = new SampleDbContextFactory())
{
using (var context = factory.CreateContext())
{
...
}
}
I have experimented amongst other things with making _connection static, using EnsureDeleted before EnsureCreated,..
What could be the issue?
For anyone else having this problem:
I had some seed object that was declared static. This kept the database alive over multiple tests. So make sure all of your seeded rows are new instances.
Related
My application is an ASP.NET Core 1.0 Web API. I would like to test the following method (snipped):
public async Task<bool> GetClientsAsync()
{
foreach (var user in await this.clientAdapter.Users().ToListAsync())
{
return true;
}
return false;
}
Normally the clientAdapter is calling UserManager<IdentityUser>'s property Users. So the code for the "real" clientAdapterlooks like that:
public IQueryable<IdentityUser> Users()
{
return this.userManager.Users;
}
Now when I am testing the clientAdapter looks like the following:
private readonly List<IdentityUser> clientList;
public TestClientAdapter(){
this.clientList= this.CreateClientList();
}
public IQueryable<IdentityUser> Users()
{
return this.userList.AsQueryable();
}
The return type of the method Users() has to be IQueryable<IdentityUser> since thats the return value of the original class UserManager<IdentityUser>. Now if I execute the test I am getting the following error, as soon as it hit's the foreach loop (the problem is the ToListAsync() call):
System.NotSupportedException: "Store does not implement IQueryableUserStore<TUser>."
If I change the loop from
foreach (var user in await this.clientAdapter.Users().ToListAsync())
{
return true;
}
to
foreach (var user in this.clientAdapter.Users().ToList())
{
return true;
}
Everything works fine.
My Problem:
I am not not able to mock the UserManager since the UserManager needs a UserStore which needs a DBContext which I dont know how to mock. And even if it was possbile to mock the DBContext, I think this would turn my unit test into an integration test and I dont want that. Plus it's probably not worth the effort. So I cannot just work with a mocked Usermanager and get the data from it.
My Question:
Is it possible to make the unit test pass, without changing the method I want to test?
EDIT
#CodeCaster:
The injected clientAdapter now looks like the following (snipped):
public class TestClientAdapter: IClientAdapter, IQueryableUserStore<IdentityUser>
{
private readonly List<IdentityUser> clientList
private UserManager<IdentityUser> testUserManager;
public TestClientAdapter: ()
{
clientList= this.CreateclientList();
this.testUserManager = new UserManager<IdentityUser>(this, null, null, null, null, null, null, null, null);
}
public IQueryable<IdentityUser> Users()
{
return this.testUserManager.Users;
}
IQueryable<IdentityUser> IQueryableUserStore<IdentityUser>.Users
{
get
{
return this.clientList.AsQueryable();
}
}
Now Iam getting another Exception:
"System.InvalidOperationException" in System.Private.CoreLib.ni.dll"
ToListAsync (among of other async methods like AnyAsync, etc.) is not a standard Linq2SQL (aka IQueryable<T>) extension method from System.Linq.*.
It's part of EntityFramework and as such it assumes certain preconditions, hence it can't work with a queryable List. Basically it's a wrapper around query.AsAsyncEnumerable() and AsAsyncEnumerable checks for the existence of IAsyncEnumerable<TSource> and/or IAsyncEnumerableAccessor<TSource> and if not there throws the invalid operation exception.
There are two things you can do...
Use EF Core InMemoryDatabase for an integration test, which was made for integration tests
Refactor your code so IQueryable<T> doesn't leak outside of your repository or command/query handlers
Technically it may be possible to create an list which implements AsAsyncEnumerable<T> but I haven't tried it and most likely not working with list.AsQueryable() since it wraps the list somewhere below...
Let the clientAdapter you inject for tests also implement IQueryableUserStore<TUser>, as the UserManager casts it to that, and if that fails, throws the mentioned exception.
My application can connect with multiple data bases (every data base have the same schema), I store the current DB, selected by user, in Session and encapsule access using a static property like:
public class DataBase
{
public static string CurrentDB
{
get
{
return HttpContext.Current.Session["CurrentDB"].ToString();
}
set
{
HttpContext.Current.Session["CurrentDB"] = value;
}
}
}
Other pieces of code access the static CurrentDB to determine what DB use.
Some actions start background process in a thread and it need access the CurrentDB to do some stuff. I'm thinking using something like this:
[ThreadStatic]
private static string _threadSafeCurrentDB;
public static string CurrentDB
{
get
{
if (HttpContext.Current == null)
return _threadSafeCurrentDB;
return HttpContext.Current.Session["CurrentDB"].ToString();
}
set
{
if (HttpContext.Current == null)
_threadSafeCurrentDB = value;
else
HttpContext.Current.Session["CurrentDB"] = value;
}
}
And start thread like:
public class MyThread
{
private string _currentDB;
private thread _thread;
public MyThread (string currentDB)
{
_currentDB = currentDB;
_thread = new Thread(DoWork);
}
public DoWork ()
{
DataBase.CurrentDB = _currentDB;
... //Do the work
}
}
This is a bad practice?
Actually, I think you should be able to determine which thread uses which database, so I would create a class inherited from Thread, but aware of the database it uses. It should have a getDB() method, so, if you need a new Thread which will use the same database as used in another specific Thread, you can use it. You should be able to setDB(db) of a Thread as well.
In the session you are using a current DB approach, which assumes that there is a single current DB. If this assumption describes the truth, then you can leave it as it is and update it whenever a new current DB is being used. If you have to use several databases in the same time, then you might want to have a Dictionary of databases, where the Value would be the DB and the Key would be some kind of code which would have a sematic meaning which you could use to be able to determine which instance is needed where.
I have a method like this:
public int InsertOrUpdateCustomer(Customer customer)
{
var result = default(int);
try
{
using (var customerContext = new Customer())
{
var customerResult = customerContext.UpdateGraph(coupon, map => map.OwnedCollection(p => p.CustomerPoints));
couponsContext.SaveChanges();
result = customerResult.CustomerTypeID;
}
}
catch (Exception ex)
{
// Log the Exception
}
return result;
}
It creates an instance of CustomerContext, Saves, and returns the new CustomerID.
I am trying to use Moq for this and have this method where the test needs to check for a integer value being returned.
[TestMethod]
public void Inserting_A_Customer_Should_Return_A_IntegerValue(Customer customer)
{
var mock = new Mock<ICustomerRepository>();
int customerId = 1;
mock.Setup(c => c.InsertOrUpdateCustomer(customer)).Returns(new Customer() { Id = customerId });
}
That gives this error:
cannot convert from 'Entities.Commerce.Customer' to 'System.Func<int>'
I am also new to Moq.
What I would like to know from this question is, if one has a code like above, how does one proceed with writing Unit Tests.
It would be of great help if some pointers are given in getting to know that process.
Thanks in advance.
The error itself is because the method you are setting up is of this signature:
public int InsertOrUpdateCustomer(Customer customer)
Whereas your setup is trying to return a customer
mock.Setup(c => c.InsertOrUpdateCustomer(customer))
.Returns(new Customer() { Id = customerId });
Changing this to return a fake int such as .Returns(42); will avoid the error.
The not so good news is if the purpose of the test is Inserting_A_Customer_Should_Return_A_IntegerValue that you will be mocking the very thing you are trying to test (you would just be testing Moq).
What you need to do is Moq out your DbContext, which makes this line problematic, given its tight coupling:
using (var customerContext = new CustomerContext())
The suggestion here is to either allow the DbContext to be injected into the constructor of your class you are testing (or inject a factory interface which can create a DbContext).
You can then Mock the DbContext and the relevant IDbSets (Customers) as per this MSDN article here, which you can then inject into your class being tested, and test any logic / branching in your class.
Hey there folks, having a little issue here which I'm trying to wrap my head around.
I'm currently starting out with nHibernate, such I have to due to work requirements, and am getting a little stuck with nHibernate's Sessions and multiple threads. Well the task I want to complete here is to have Log4Net log everything to the database, including nHibernate's debug/errors etc.
So what I did was create a very simple Log4Net:AppenderSkeleton class which fires perfectly when I need it. My intial issue I ran into was that when I used GetCurrentSession, obviously since Log4Net runs on a seperate thread(s), it errored out with the initial thread's session. So I figured that I had to create a new nHiberante Session for the Log4Net AppenderSkeleton class. The code is below:
public class Custom : AppenderSkeleton
{
protected override void Append(LoggingEvent loggingEvent)
{
if (loggingEvent != null)
{
using (ISession session = NHibernateHelper.OpenSession())
{
using (ITransaction tran = session.BeginTransaction())
{
Log data = new Log
{
Date = loggingEvent.TimeStamp,
Level = loggingEvent.Level.ToString(),
Logger = loggingEvent.LoggerName,
Thread = loggingEvent.ThreadName,
Message = loggingEvent.MessageObject.ToString()
};
if (loggingEvent.ExceptionObject != null)
{
data.Exception = loggingEvent.ExceptionObject.ToString();
}
session.Save(data);
tran.Commit();
}
}
}
}
Simple enough idea really, while it is at its basic form now, I will have more error checking info etc but for now the issue is that while this works perfectly it creates multiple sessions. That is, it creates a new session per error logged since I can't use GetCurrentSession as this will get the calling Session (the main program flow). I'm sure there is a way for me to create a session globally for Log4Net's thread, but I'm unsure gow to. Keeping in mind that I already bind a Session to the intial thread using the below in Global.asax (Application_BeginRequest):
ISession session = NHibernateHelper.OpenSession();
CurrentSessionContext.Bind(session);
And for those that will ask, the contents of my helper is below (this is in a DLL):
public static class NHibernateHelper
{
private static Configuration _nHibernateConfig;
private static ISessionFactory _nHibernateSessionFactory;
private static ISessionFactory BuildNHibernateSessionFactory
{
get
{
if (_nHibernateSessionFactory == null)
{
if (_nHibernateConfig == null)
{
BuildSessionFactory();
}
_nHibernateSessionFactory = _nHibernateConfig.BuildSessionFactory();
}
return _nHibernateSessionFactory;
}
}
private static Configuration BuildNHibernateConfig
{
get
{
if (_nHibernateConfig == null)
{
_nHibernateConfig = new ConfigurationBuilder().Build();
}
return _nHibernateConfig;
}
}
public static Configuration nHibernateConfig
{
get
{
return _nHibernateConfig;
}
}
public static ISessionFactory nHibernateSessionFactory
{
get
{
return _nHibernateSessionFactory;
}
}
public static Configuration BuildConfiguration()
{
return BuildNHibernateConfig;
}
public static ISessionFactory BuildSessionFactory()
{
return BuildNHibernateSessionFactory;
}
public static ISession OpenSession()
{
return _nHibernateSessionFactory.OpenSession();
}
public static ISession GetCurrentSession()
{
try
{
return _nHibernateSessionFactory.GetCurrentSession();
}
catch (HibernateException ex)
{
if(ex.Message == "No session bound to the current context")
{
// See if we can bind a session before complete failure
return _nHibernateSessionFactory.OpenSession();
}
else
{
throw;
}
}
catch (Exception ex)
{
throw;
}
}
}
I realise I could use the ADO appender in log4net but I wish to use nHibernate directly to add the data to the database. The reason being is I don't wish to mess around with connectionstrings etc when nHibernate is already working.
As always, any help is always appreciated.
-- Edit: --
So based on what I have been told initialy, I modified my custom Log4Net logger code. There are two versions below. My question, which is best or is there a better way?
The first, according to nHibernate Prof, creates only two sessions - The intial session is for the main program flow as intended and the second for my Log4Net logger code. Yet this has hundreds of enteries in the second session and complains about too many enteries and to many calls to the database.
The second, nHibernate prof shows many sessions, as many sessions as there are calls to the logger +1 for the main program flow. Yet no complaints anywhere on nHprof. Though I have this feeling that having that many sessions would have people frowning or is too much tasking.
Anyway the codes:
Code 1 -
protected override void Append(LoggingEvent loggingEvent)
{
if (!System.Web.HttpContext.Current.Items.Contains("Log4Net nHibernate Session"))
{
System.Web.HttpContext.Current.Items.Add("Log4Net nHibernate Session", NHibernateHelper.OpenStatelessSession());
}
IStatelessSession statelessSession = System.Web.HttpContext.Current.Items["Log4Net nHibernate Session"] as IStatelessSession;
if (statelessSession != null && loggingEvent != null)
{
using (ITransaction tran = statelessSession.BeginTransaction())
{
Log data = new Log
{
Id = Guid.NewGuid(),
Date = loggingEvent.TimeStamp,
Level = loggingEvent.Level.ToString(),
Logger = loggingEvent.LoggerName,
Thread = loggingEvent.ThreadName,
Message = loggingEvent.MessageObject.ToString()
};
if (loggingEvent.ExceptionObject != null)
{
data.Exception = loggingEvent.ExceptionObject.ToString();
}
statelessSession.Insert(data);
tran.Commit();
}
}
}
Code 2 -
protected override void Append(LoggingEvent loggingEvent)
{
if (loggingEvent != null)
{
using (IStatelessSession statelessSession = NHibernateHelper.OpenStatelessSession())
using (ITransaction tran = statelessSession.BeginTransaction())
{
Log data = new Log
{
Id = Guid.NewGuid(),
Date = loggingEvent.TimeStamp,
Level = loggingEvent.Level.ToString(),
Logger = loggingEvent.LoggerName,
Thread = loggingEvent.ThreadName,
Message = loggingEvent.MessageObject.ToString()
};
if (loggingEvent.ExceptionObject != null)
{
data.Exception = loggingEvent.ExceptionObject.ToString();
}
statelessSession.Insert(data);
tran.Commit();
}
}
}
You are right about creating a new session. You definitely don't want to share the same session across threads. In your logging instance I would even say to use an IStatelessSession. Also sessions should be fairly lightweight so I wouldn't worry about creating new sessions each time you log a statement.
NHibernate already uses Log4Net internally so you just need to enable the logger and use an AdoNetAppender to send the logs to your database.
<log4net>
<appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">
...
</appender>
<logger name="NHibernate">
<level value="WARN"/>
<appender-ref ref="AdoNetAppender"/>
</logger>
</log4net>
I am using SQLite as my db during development, and I want to postpone actually creating a final database until my domains are fully mapped. So I have this in my Global.asax.cs file:
private void InitializeNHibernateSession()
{
Configuration cfg = NHibernateSession.Init(
webSessionStorage,
new [] { Server.MapPath("~/bin/MyNamespace.Data.dll") },
new AutoPersistenceModelGenerator().Generate(),
Server.MapPath("~/NHibernate.config"));
if (ConfigurationManager.AppSettings["DbGen"] == "true")
{
var export = new SchemaExport(cfg);
export.Execute(true, true, false, NHibernateSession.Current.Connection, File.CreateText(#"DDL.sql"));
}
}
The AutoPersistenceModelGenerator hooks up the various conventions, including a TableNameConvention like so:
public void Apply(FluentNHibernate.Conventions.Instances.IClassInstance instance)
{
instance.Table(Inflector.Net.Inflector.Pluralize(instance.EntityType.Name));
}
This is working nicely execpt that the sqlite db generated does not have pluralized table names.
Any idea what I'm missing?
Thanks.
Well, I'm not sure why this made a difference, but in the process of debugging, I did this, and now it works:
public void Apply(FluentNHibernate.Conventions.Instances.IClassInstance instance)
{
string tablename = Inflector.Net.Inflector.Pluralize(instance.EntityType.Name);
instance.Table(tablename);
System.Diagnostics.Debug.WriteLine(string.Format("Table = {0}", instance.TableName));
}