Unit Testing
TestInitialize
Test Class Constructor
Software Testing
Test Preparation

Do you use TestInitialize or the test class constructor to prepare each test? and why?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In MSTest, both the test class constructor and TestInitialize run before each test method, so the question is not whether either one works. The real question is what kind of setup you need and which lifecycle behavior makes the test easier to understand and maintain.

The Order of Execution Matters

MSTest creates a new instance of the test class for each test. The constructor runs first, then MSTest sets TestContext, and then any TestInitialize methods run before the test method itself.

That gives you two different setup hooks with different capabilities:

  • Constructor for simple synchronous object construction
  • 'TestInitialize for framework-aware per-test setup'

A small example makes the distinction clear:

csharp
1[TestClass]
2public class CalculatorTests
3{
4    private readonly Calculator _calculator;
5
6    public CalculatorTests()
7    {
8        _calculator = new Calculator();
9    }
10
11    [TestMethod]
12    public void Add_TwoNumbers_ReturnsSum()
13    {
14        Assert.AreEqual(5, _calculator.Add(2, 3));
15    }
16}

This is a good constructor use case. The test depends on a simple object that can be created synchronously and stored in a readonly field.

Why Constructors Are Often the Better Default

For straightforward setup, constructors are attractive because they are ordinary C# and they encourage immutability.

Benefits of constructor-based setup:

  • Easy to read because it is standard object initialization
  • Works naturally with readonly fields
  • Keeps simple setup close to the field declarations
  • Avoids lifecycle attributes when you do not need them

That last point matters. A lot of test code becomes noisier than necessary because every piece of setup is pushed into framework hooks even when the setup is just "new up a dependency."

When TestInitialize Is the Better Choice

TestInitialize becomes the right tool when the setup is not just construction. The biggest reasons are async work, access to MSTest features, or behavior that depends on framework ordering.

csharp
1[TestClass]
2public class ApiTests
3{
4    private ApiClient _client = null!;
5    public TestContext TestContext { get; set; } = null!;
6
7    [TestInitialize]
8    public async Task InitAsync()
9    {
10        _client = new ApiClient();
11        await _client.ConnectAsync();
12        TestContext.WriteLine("Client connected");
13    }
14
15    [TestMethod]
16    public async Task Ping_ReturnsOk()
17    {
18        var response = await _client.PingAsync();
19        Assert.AreEqual("OK", response);
20    }
21}

This setup belongs in TestInitialize because:

  • The work is asynchronous
  • 'TestContext is available'
  • You may want MSTest attributes such as timeout support on the initialization method

Those are things the constructor cannot do well.

A Useful Rule of Thumb

A practical rule is:

  • Use the constructor for simple, synchronous, always-needed setup
  • Use TestInitialize when setup is async, framework-aware, or more operational than structural

You can also combine them. That is often the cleanest design.

csharp
1[TestClass]
2public class RepositoryTests
3{
4    private readonly InMemoryDatabase _db;
5    private Repository _repository = null!;
6
7    public RepositoryTests()
8    {
9        _db = new InMemoryDatabase();
10    }
11
12    [TestInitialize]
13    public void Init()
14    {
15        _repository = new Repository(_db);
16        _db.Reset();
17    }
18}

Here the constructor creates durable per-test infrastructure, while TestInitialize prepares mutable state that should be refreshed before each test method.

Exception and Cleanup Behavior

One subtle difference is failure handling. If the constructor throws, the test instance never fully exists, so later cleanup hooks are more limited. If TestInitialize throws, MSTest still has a created test instance and cleanup behavior is more predictable.

That does not mean constructors are risky; it just means setup logic that can fail in more operational ways often fits better in TestInitialize.

Another important point is TestContext. Because MSTest sets TestContext after constructing the test instance, code in the constructor should not rely on it. If you need the current test name, output logging, or similar runtime information, TestInitialize is the correct hook.

Avoid Overengineering Test Setup

The worst pattern is not choosing one hook over the other. The worst pattern is stuffing too much work into either of them:

  • Slow I/O in every test when it could be moved to class-level setup
  • Hidden global state changes
  • Complex branching that makes tests hard to reason about

Per-test setup should be short, explicit, and local to the behavior under test. If many tests need expensive shared fixtures, that is usually a sign to look at ClassInitialize, disposable fixtures, or a different test design.

Common Pitfalls

  • Using TestInitialize for trivial object creation that would be clearer in a constructor.
  • Using the constructor for async setup, which it does not support cleanly.
  • Assuming TestContext is available inside the constructor.
  • Mixing mutable test state into readonly setup without a clear reason.
  • Putting too much expensive work into per-test setup instead of moving it to a broader lifecycle hook.

Summary

  • Both the constructor and TestInitialize run before each MSTest test method.
  • Constructors are usually better for simple synchronous setup and readonly fields.
  • 'TestInitialize is better for async setup, TestContext, and framework-aware initialization.'
  • Combining both is often the cleanest approach.
  • The right choice depends less on preference and more on the kind of setup you actually need.

Course illustration
Course illustration

All Rights Reserved.