Question

I've got two test cases for a controller. I want to make sure that each test case is receiving a unique Session instance in order to simulate InstancePerWebApiRequest. My current implementation is flawed because it works with a single ISession instance throughout multiple test cases. However, if I dispose of the Scope after one test case then I am told my Session is closed even though I have re-requested a new one from Autofac.

Here's what I've got:

public class AutofacRegistrations
{
    public static void RegisterAndSetResolver()
    {
        var containerBuilder = new ContainerBuilder();

        containerBuilder.RegisterApiControllers(Assembly.GetExecutingAssembly());

        //  Per Design Patterns: Elements of Reusable Object-Oriented Software - an abstract factory is often used as a singleton.
        containerBuilder.Register(x => new NHibernateConfiguration().Configure().BuildSessionFactory()).SingleInstance();
        containerBuilder.RegisterType<NHibernateDaoFactory>().As<IDaoFactory>().SingleInstance();
        containerBuilder.RegisterType<StreamusManagerFactory>().As<IManagerFactory>().SingleInstance();

        //  Everything else wants an instance of Session per API request, so indicate that:
        containerBuilder.Register(x => x.Resolve<ISessionFactory>().OpenSession()).InstancePerApiRequest();
        containerBuilder.Register(x => LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType)).InstancePerApiRequest();

        ILifetimeScope container = containerBuilder.Build();

        var dependencyResolver = new AutofacWebApiDependencyResolver(container);
        GlobalConfiguration.Configuration.DependencyResolver = dependencyResolver;
    }
}

Here I declare my factories as singletons and indicate that I want an ISession per api request.

Here's how I setup my test:

public abstract class StreamusTest
{
    protected ILog Logger;
    protected IDaoFactory DaoFactory;
    protected IManagerFactory ManagerFactory;
    protected Helpers Helpers;
    protected ISession Session;

    private IDependencyScope Scope;

    [TestFixtureSetUp]
    public void TestFixtureSetUp()
    {
        StreamusWebApi.InitializeApplication();

        //  TODO: I feel like this is incorrect because Session is only generated once. Shouldn't it be SessionPerTestCase? 
        Scope = GlobalConfiguration.Configuration.DependencyResolver.BeginScope();

        Logger = (ILog)Scope.GetService(typeof(ILog));
        DaoFactory = (IDaoFactory)Scope.GetService(typeof(IDaoFactory));
        ManagerFactory = (IManagerFactory)Scope.GetService(typeof(IManagerFactory));
        Session = (ISession)Scope.GetService(typeof(ISession));

        Helpers = new Helpers(Logger, Session, ManagerFactory);
    }

    [SetUp]
    public void SetUp()
    {
        //  TODO: I think I want to be generating a Scope per SetUp and dispose of it in TearDown.
        //  Scope = GlobalConfiguration.Configuration.DependencyResolver.BeginScope();
        //  Logger = (ILog)Scope.GetService(typeof(ILog));
        //  Session = (ISession)Scope.GetService(typeof(ISession));
    }

    [TearDown]
    public void TearDown()
    {
        //  Scope.Dispose(); 
    }

    [TestFixtureTearDown]
    public void TestFixtureTearDown()
    {
        Scope.Dispose(); 
    }
}

I feel like this is incorrect because I am only instantiating my Scope once in TestFixtureSetup. If I dispose of my scope after each test case (in TearDown) then my 2nd test case throws an exception indicating that Session is closed.

I believe my current implementation uses a single Session throughout all of my test cases which would be incorrect. Can anyone offer some advice on how to properly set this up?

Here are my actual test cases, for reference:

[TestFixture]
public class ClientErrorControllerTest : StreamusTest
{
    private ClientErrorController ClientErrorController;

    [SetUp]
    public new void TestFixtureSetUp()
    {
        ClientErrorController = new ClientErrorController(Logger, Session, ManagerFactory);
    }

    [Test]
    public void CreateError_ShortMessage_ErrorCreated()
    {
        var clientErrorDto = new ClientErrorDto
            {
                Message = "Hello World",
                ClientVersion = "0.99",
                LineNumber = 2
            };

        ClientErrorDto createdErrorDto = ClientErrorController.Create(clientErrorDto);
        Assert.NotNull(createdErrorDto);
    }

    [Test]
    public void CreateError_LongMessage_MessageTruncatedErrorCreated()
    {
        var clientErrorDto = new ClientErrorDto
            {
                Message =
                    "Hello World This is a Really Long Message Which is going to be over 255 characters in length when finished which will cause the end result message to be truncated with three periods as to not overflow the database. Can I confirm that this is happening? Thanks",
                ClientVersion = "0.99",
                LineNumber = 2
            };

        ClientErrorDto createdErrorDto = ClientErrorController.Create(clientErrorDto);

        Assert.NotNull(createdErrorDto);
        Assert.That(createdErrorDto.Message.Length == 255);
    }
}
Was it helpful?

Solution

This appears somewhat similar/related to your other question about getting per-request scopes. The answer here is the same as before - you need to use HttpRequestMessage to manage lifetime scope. That means resolving your controller from the lifetime scope as well, which will, in turn, resolve all the dependencies registered as InstancePerApiRequest.

Also, as mentioned in the previous answer, I'd really stay away from setting global static variables (the global dependency resolver). You can really mess up a test environment and get odd/hard to reproduce issues or intermittent failures when you set up statics.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top