xunit constructor runs before each test
Asked Answered
E

1

48

I have a test class where the constructor is called before each test execution. The data initialized by the constructor is currently not shared among the following tests. I want this initialized data to be set up once and shared across all tests. Here's my code:

[Category("Basics")]
[Collection("DBCollection")]
[ExcludeFromCodeCoverage]
public class SecurityTests : TestUnitBase
{
    StartUpFixture fixture;
    public AuthenticationTests(StartUpFixture fixture)
       : base()
    {
        this.fixture = fixture;
    }

    [Fact(DisplayName = "Successful response Test1")]
    public void SuccessfulResponseTest1()
    {
        var users = base.Db.Users.FirstOrDefault(x => x.Name == "abc");
        ...
    }

    [Fact(DisplayName = "Successful response Test2")]
    public void SuccessfulResponseTest2()
    {
        var users = base.Db.Users.FirstOrDefault(x => x.Name == "xyz");
        ...
    }

Can you help me set it up so that the fixture is initialized once and its data is available to all test methods in this class?

Thanks in advance.

Economist answered 25/10, 2017 at 7:51 Comment(3)
When creating an instance of a derived class you have to call a constructor of the base-class. XUnit isn´t different on this. However you may simply not use the constructor at all but the Setup-methods?Tita
it´s the normal way that each testcase starting with a new initialized object. you don´t know the cronology of the cases to run so you should not create tests which need to be run in a row and manipulate class under test. if some cases got other inits - recreate/specify this in the testcasemethod and go on.Kick
XUnit executes tests in parallel by default. Having shared state/values between tests can lead to strange behaviour, failed tests with no reasons etc.Jamboree
F
58

You can use Xunit Class fixtures. When using a class fixture, xUnit.net will ensure that the fixture instance will be created before any of the tests have run, and once all the tests have finished, it will clean up the fixture object by calling Dispose, if present. For example:

//similar to base class
public class DatabaseFixture : IDisposable
{
    public SqlConnection Db { get; private set; }
    public DatabaseFixture()
    {
        Db = new SqlConnection("MyConnectionString");

        // initialize data in the test database
    }

    public void Dispose()
    {
        // clean up test data from the database
    }
}

//Class where you want to use shared class instance
public class MyDatabaseTests : IClassFixture<DatabaseFixture>
{
    DatabaseFixture dbFixture;

    public MyDatabaseTests(DatabaseFixture fixture)
    {
        this.dbFixture = fixture;
    }

    // write tests, using dbFixture.Db to get access to the SQL Server
}

For each test, it will create a new instance of MyDatabaseTests, and pass the shared instance of DatabaseFixture to the constructor.

Xunit Documentation here.

Hope it helps.

Edit 28/10 For multiple fixtures you can create a class which encapsulates the other two fixtures as below and create startup fixture to run before db fixture:

public class SecurityTestsFixture : IDisposable
{
    public DatabaseFixture Dbfixture { get; private set; }
    public StartupTestFixture Startupfixture { get; private set; }

    public SecurityTestsFixture()
    {
        Startupfixture = new StartupTestFixture();
        Dbfixture = new DatabaseFixture(Startupfixture);
    }

    public void Dispose()
    {
        // clean up code
        Dbfixture.Dispose();
    }

    public class StartupTestFixture
    {
        public string SqlConnString { get; private set; }
        public StartupTestFixture()
        {
            SqlConnString = ConfigurationManager.AppSettings["SqlConnectionString"];

            // other startup code
        }
    }

    public class DatabaseFixture : IDisposable
    {
        public SqlConnection Db { get; private set; }
        public DatabaseFixture(StartupTestFixture sFixture)
        {
            Db = new SqlConnection(sFixture.SqlConnString);

            // initialize data in the test database
        }

        public void Dispose()
        {
            // clean up test data from the database
        }
    }
}

and test class:

public class Xunittests : IClassFixture<SecurityTestsFixture>
{
    SecurityTestsFixture _securityFixture;
    public Xunittests(SecurityTestsFixture securityfixture)
    {
        _securityFixture = securityfixture;
    }

    [Fact(DisplayName = "Successful response Test1")]
    public void SuccessfulResponseTest1()
    {
        var db = _securityFixture.Dbfixture.Db;
        //var users = db.Users.FirstOrDefault(x => x.Name == "...");
        Assert.Equal("test", "test");
    }
}
Filiano answered 27/10, 2017 at 11:42 Comment(2)
I used fixture for something else, as you can see it's StartUpFixture in code, is it possible to have muliple fixture? and run them in order? first startUpFixture then DbFixtureEconomist
This workes well if your fixture object is not to be shared between classes. For my problem ideal case would be in your Selenium, web UI testing. When we have placed the driver object in the base class which intern implements this IClassFixture, how can this driver object be accessed in other classes which inherits the base class?Myriad

© 2022 - 2024 — McMap. All rights reserved.