I would like to create an NHibernate session factory once at the start of a SpecFlow test run, and then access it in individual step definitions to call OpenSession() on it.
It seems like a [BeforeTestRun]
hook would be the best place to set up the session factory. However I am struggling to see how I can store the session factory and then retrieve it in a particular step definition (most likely part of a Background
section) in order to get a session and insert some data.
I tried using the SpecFlow container, as follows:
[Binding]
public class NhDataSupport
{
private readonly IObjectContainer objectContainer;
public NhDataSupport(IObjectContainer objectContainer)
{
this.objectContainer = objectContainer;
}
[BeforeTestRun]
public void InitializeSessionFactory()
{
var sessionFactory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2012.ConnectionString(c => c.FromConnectionStringWithKey("SqlServerDataTesting")))
.Mappings(cfg =>
cfg.FluentMappings.AddFromAssemblyOf<HostMap>()
)
.BuildSessionFactory();
objectContainer.RegisterInstanceAs<ISessionFactory>(sessionFactory);
}
}
...so that other [Binding]
classes could be passed the session factory via constructor injection, I hoped. But this gets a
System.Reflection.TargetException, Non-static method requires a target.
I'm guessing that's because (as I learned from the SpecFlow docs), the method [BeforeTestRun]
is applied to must be static.
Is there a way of achieving this, configuring the SessionFactory once but calling OpenSession on it from other Binding classes? I don't want to build the session factory for every scenario, as this is an expensive operation.
You could do something like this:
public class SessionFactoryHolder
{
private static ISessionFactory sessionFactory;
public static void SetupNhibernateSessionFactory()
{
sessionFactory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2012.ConnectionString(c => c.FromConnectionStringWithKey("SqlServerDataTesting")))
.Mappings(cfg => cfg.FluentMappings.AddFromAssemblyOf<HostMap>() )
.BuildSessionFactory();
}
public ISessionFactory SessionFactory
{
get { return sessionFactory; }
}
}
[Binding]
public class Binding
{
[BeforeTestRun]
public static void SetupNhibernateSessionFactory()
{
SessionFactoryHolder.SetupNhibernateSessionFactory();
}
}
Now you can access the SessionFactory when you let SpecFlow inject the SessionFactoryHolder via constructor.
It is similar to @ngm solution, but you can spare to get the "internal" IObjectContainer from SpecFlow.
See here http://www.specflow.org/documentation/Context-Injection/ for more infos about context injection in SpecFlow.
Notice: code written by head, not tried to compile, so there could be typos.