[C#]Setup and Teardown in xUnit.net (Constructor and Dispose)

目次

Overview

In xUnit.net, a new instance of the test class is created for every test method and destroyed once the test is complete. By utilizing this lifecycle, it is standard practice to write “Setup” in the constructor and “Teardown” in the Dispose method of the IDisposable interface.

Specifications (Lifecycle)

  • Setup: Written in the constructor (public ClassName() { ... }). It is called before every test method.
  • Teardown: Written in the Dispose() method. It is called after every test method finishes.
  • Interface: The class must implement the IDisposable interface.

Basic Usage

public class MyTests : IDisposable
{
    // Constructor: Setup
    public MyTests()
    {
        // Initialize database connections or lists
    }

    // Dispose: Teardown
    public void Dispose()
    {
        // Delete files or close connections
    }

    [Fact]
    public void Test1() { /* ... */ }
}

Full Code

The following example shows how to initialize a Stack<int> as a setup process so that each test method uses a clean instance.

using System;
using System.Collections.Generic;
using Xunit;

namespace UnitTestPatterns
{
    // Implement IDisposable to enable teardown processing
    public class StackTests : IDisposable
    {
        private Stack<int> _stack;

        /// <summary>
        /// [Setup]
        /// The constructor is called every time a test method (Test1, Test2) runs.
        /// A new instance of _stack is created each time.
        /// </summary>
        public StackTests()
        {
            _stack = new Stack<int>();
            Console.WriteLine("Setup: Stack initialized.");
        }

        /// <summary>
        /// [Teardown]
        /// This is called every time a test method finishes.
        /// Use this to release resources.
        /// </summary>
        public void Dispose()
        {
            _stack = null; // Shown as an example
            Console.WriteLine("Teardown: Resources released.");
        }

        [Fact]
        public void Push_OneItem_CountIsOne()
        {
            // Arrange (Completed in the constructor)

            // Act
            _stack.Push(42);
            var count = _stack.Count;

            // Assert
            Assert.Equal(1, count);
        }

        [Fact]
        public void Pop_OneItem_ReturnsValueAndCountIsZero()
        {
            // Arrange
            _stack.Push(11);

            // Act
            var val = _stack.Pop();
            var count = _stack.Count;

            // Assert
            Assert.Equal(11, val); // The value should be 11
            Assert.Equal(0, count); // The stack should be empty
        }
    }
}

Customization Points

  • Class Fixtures (IClassFixture): While constructors run for every test method, use IClassFixture<T> for heavy tasks (like database connections) that you want to run only once per test class.
  • Collection Fixtures: Use the [Collection] attribute if you need to share a context across multiple test classes.

Points of Caution

  • Instances are Disposable: xUnit creates a new instance for every test method. Changes made to _stack in Test1 do not affect _stack when Test2 runs; it always starts from an empty state.
  • Need for Dispose: If you are only handling simple objects in memory (like List or Stack), the Garbage Collector handles them, so you do not strictly need to implement Dispose. Use it only for unmanaged resources like file handles or database connections.

Application

Asynchronous Initialization and Cleanup

If your setup requires asynchronous processing (async/await), implement the IAsyncLifetime interface.

public class AsyncTests : IAsyncLifetime
{
    public async Task InitializeAsync()
    {
        // Async setup (e.g., populating a database)
        await PrepareDatabaseAsync();
    }

    public async Task DisposeAsync()
    {
        // Async cleanup
        await CleanDatabaseAsync();
    }
    
    // ... [Fact] methods ...
}

Summary

In xUnit.net, write setup logic in the constructor and teardown logic in the Dispose method. This design of rebuilding the environment for every method eliminates dependencies between tests (side effects) and ensures that every test produces the same result regardless of the execution order.

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次