Ich möchte zu Beginn eines SpecFlow-Testlaufs einmal eine NHibernate-Sitzungsfactory erstellen und dann in einzelnen Schrittdefinitionen darauf zugreifen, um OpenSession () darauf aufzurufen.

Es scheint, als wäre ein [BeforeTestRun] Hook der beste Ort, um die Session Factory einzurichten. Ich habe jedoch Schwierigkeiten zu sehen, wie ich die Sitzungsfactory speichern und dann in einer bestimmten Schrittdefinition (höchstwahrscheinlich Teil eines Background - Abschnitts) abrufen kann, um eine Sitzung abzurufen und einige Daten einzufügen.

Ich habe versucht, den SpecFlow-Container wie folgt zu verwenden:

[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);
    }
}

... damit andere [Binding] Klassen per Konstruktorinjektion an die Session Factory übergeben werden konnten, hoffte ich. Aber das bekommt ein

System.Reflection.TargetException, Nicht statische Methode erfordert ein Ziel.

Ich vermute, das liegt daran, dass (wie ich aus den SpecFlow-Dokumenten gelernt habe) die Methode {{ X0}} muss statisch sein.

Gibt es eine Möglichkeit, dies zu erreichen, indem Sie die SessionFactory einmal konfigurieren, aber OpenSession von anderen Bindungsklassen darauf aufrufen? Ich möchte die Session Factory nicht für jedes Szenario erstellen, da dies eine teure Operation ist.

3
ngm 30 Dez. 2015 im 01:13

3 Antworten

Beste Antwort

Sie könnten so etwas tun:

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();
     }
}

Jetzt können Sie auf die SessionFactory zugreifen, wenn Sie SpecFlow den SessionFactoryHolder über den Konstruktor injizieren lassen.

Es ähnelt der @ ngm-Lösung, Sie können jedoch sparen, um den "internen" IObjectContainer von SpecFlow zu erhalten.

Siehe hier http://www.specflow.org/documentation/Context-Injection/ Weitere Informationen zur Kontextinjektion in SpecFlow.

Hinweis: Code, der von head geschrieben wurde und nicht versucht wurde zu kompilieren, daher kann es zu Tippfehlern kommen.

2
Andreas Willich 14 Jän. 2016 im 12:49

Folgendes funktioniert.

  • Verwenden Sie ein statisches Feld für eine nicht statische [Binding] - kommentierte Klasse.
  • Führen Sie in [BeforeTestRun] die Arbeit aus (in meinem Fall das Erstellen von SessionFactory) und weisen Sie das Ergebnis dem statischen Feld zu.
  • Registrieren Sie in [BeforeScenario] die statische Feldinstanz im Container.

Ich bin mir nicht sicher, ob es die beste Vorgehensweise ist, aber es funktioniert.

[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);
    }
}

Die Session Factory ist dann in jeder [Binding] - annotierten Klasse über die Konstruktorinjektion von ISessionFactory verfügbar.

3
ngm 3 Jän. 2016 im 16:13

Obwohl dies nicht spezifisch für NHibernate ist, stieß ich auf ein ähnliches Problem, bei dem versucht wurde, die Autorisierung während eines gesamten Testlaufs von API-Tests aufrechtzuerhalten. Am Ende habe ich ein Singleton-Muster für meinen Rest-Client mit dem Tag [BeforeScenario] verwendet. Obwohl dies nicht [BeforeTestRun] ist, nach dem diese Frage gestellt wird, und das Objekt vor jedem Szenario weiterhin registriert wird, ist die Häufigkeit, mit der der Client erstellt wird, immer noch auf einmal beschränkt. Ich würde mir vorstellen, dass Sie einen ähnlichen Ansatz auf NHibernate anwenden könnten.

[Binding]
public class RestClientInjector
{
    private readonly IObjectContainer objectContainer;
    public RestClientInjector(IObjectContainer objectContainer)
    {
        this.objectContainer = objectContainer;
    }

    [BeforeScenario]
    public void InitializeRestClient()
    {
        RestClient client = SingletonRestClient.getInstance();
        objectContainer.RegisterInstanceAs<RestClient>(client);
    }

    // implelent singleton

    public class SingletonRestClient
    {
        private static RestClient client = new RestClient();

        private SingletonRestClient(IObjectContainer objectContainer) {}

        public static RestClient getInstance()
        {
            return client;
        }
    }
}
0
Travis White 3 Juni 2017 im 02:36