Search code examples
c#nhibernatebddspecflow

How to create something in BeforeTestRun and access it in step definitions


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.


Solution

  • 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.