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.
The following works.
[Binding]-annotated class.  [BeforeTestRun], do the work (in my case building the SessionFactory) and assign the result to the static field.  [BeforeScenario], register the static field instance with the container.Not sure if it's best practice, but it does work.
[Binding]
public class DataHooks
{
    private readonly IObjectContainer objectContainer;
    private static ISessionFactory sessionFactory;
    public DataHooks(IObjectContainer objectContainer)
    {
        this.objectContainer = objectContainer;
    }
    [BeforeTestRun]
    public static void SetupNhibernateSessionFactory()
    {
        sessionFactory = Fluently.Configure()
            .Database(MsSqlConfiguration.MsSql2012.ConnectionString(c => c.FromConnectionStringWithKey("SqlServerDataTesting")))
            .Mappings(cfg =>
                cfg.FluentMappings.AddFromAssemblyOf<HostMap>()
            )
            .BuildSessionFactory();
    }
    [BeforeScenario]
    public void BeforeScenario()
    {
        objectContainer.RegisterInstanceAs<ISessionFactory>(sessionFactory);
    }
}
The session factory is then available in any [Binding]-annotated class via constructor injection of ISessionFactory.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With