Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 226
ngm Avatar asked Nov 01 '25 17:11

ngm


2 Answers

The following works.

  • Use a static field on a non-static [Binding]-annotated class.
  • In [BeforeTestRun], do the work (in my case building the SessionFactory) and assign the result to the static field.
  • In [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.

like image 62
ngm Avatar answered Nov 03 '25 09:11

ngm


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.

like image 41
Andreas Willich Avatar answered Nov 03 '25 08:11

Andreas Willich