如何在BeforeTestRun中创建内容并在步骤定义中访问它

本文关键字:定义 访问 BeforeTestRun 创建 | 更新日期: 2023-09-27 18:28:29

我想在SpecFlow测试运行开始时创建一个NHibernate会话工厂,然后在各个步骤定义中访问它,在它上调用OpenSession()。

[BeforeTestRun]挂钩似乎是设置会话工厂的最佳位置。然而,我很难理解如何存储会话工厂,然后在特定的步骤定义中检索它(很可能是Background部分的一部分),以便获得会话并插入一些数据。

我尝试使用SpecFlow容器,如下所示:

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

以便其他[Binding]类可以通过构造函数注入通过会话工厂,我希望如此。但这得到了

System.Reflection.TargetException,非静态方法需要一个目标。

我猜这是因为(正如我从SpecFlow文档中了解到的),应用[BeforeTestRun]的方法必须是静态的。

有没有一种方法可以实现这一点,只配置一次SessionFactory,但从其他Binding类调用它的OpenSession?我不想为每个场景构建会话工厂,因为这是一个昂贵的操作。

如何在BeforeTestRun中创建内容并在步骤定义中访问它

以下工作原理。

  • 在非静态[Binding]注释类上使用静态字段
  • [BeforeTestRun]中,完成工作(在我的例子中是构建SessionFactory)并将结果分配给静态字段
  • [BeforeScenario]中,向容器注册静态字段实例

不确定这是否是最佳实践,但它确实有效。

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

然后,通过ISessionFactory的构造函数注入,会话工厂在任何带有[Binding]注释的类中都可用。

您可以这样做:

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

现在,当您让SpecFlow通过构造函数注入SessionFactoryHolder时,您可以访问SessionFactory。

它类似于@ngm解决方案,但您可以从SpecFlow中获得"内部"IObjectContainer。

请参阅此处http://www.specflow.org/documentation/Context-Injection/有关SpecFlow中上下文注入的更多信息。

注意:代码由head编写,未尝试编译,因此可能存在拼写错误。

虽然这不是NHibernate特有的,但我在API测试的整个测试运行中试图维护授权时遇到了类似的问题。最后,我使用[BeforeScenario]标记为我的rest客户端使用了一个singleton模式。虽然这不是这个问题所问的[BeforeTestRun],并且在每个场景之前仍然会注册对象,但创建客户端的次数仍然限制在一次。我想你可以对NHibernate应用类似的方法。

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