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

12

83

This question regards unit testing in Visual Studio using MSTest (this is important, because of MSTest's execution order). Both the method marked [TestInitialize] and the test class constructor will run before each test method.

So, the question is, what do you tend to do in each of these areas? Do you avoid performing certain activities in either? What is your reason: style, technical, superstition?

Dichromate answered 2/12, 2008 at 16:15 Comment(1)
Better not to use TestInitialize. Avoid/minimise use of member variables in test class. Use local variables inside the test methods as much as possible. It will make each test more independent/separate/self-contained #46976311Heighttopaper
D
66

The constructor is just a structure provided by the language. Every test framework seems has its own controlled lifecycle "initialize". You'll probably only get into trouble using the constructor to mutate your locals.

MSTest: You get an entire new instance of the test class for every TestMethod. This might be the only case where it's ok to mutate your locals in the constructor, initializer, or test method and not affect the other test methods.

public class TestsForWhatever
{
    public TestsForWhatever()
    {
        // You get one of these per test method, yay!
    }

    [TestInitialize] 
    public void Initialize() 
    {
        // and one of these too! 
    }

    [TestMethod]
    public void AssertItDoesSomething() { }

    [TestMethod]
    public void AssertItDoesSomethingElse() { }
}

MSpec: You only get one Establish and Because for all your assertions (It). So, don't mutate your locals in your assertions. And don't depend on mutations of locals in base contexts (if you use them).

[Subject(typeof(Whatever))]
public class When_doing_whatever
{
    Establish context = () => 
    { 
        // one of these for all your Its
    };

    Because of = () => _subject.DoWhatever();

    It should_do_something;
    It should_do_something_else;
}
Dichromate answered 16/8, 2011 at 14:15 Comment(6)
I had the same issue and you just provided the right explanation, thanks!Jeseniajesh
You might also point out that [ClassInitialize] is only run once per Test Run (prior to the everything), so it can be used for any expensive setup routines.Sybaris
-1: You should not hate your question; it makes much sense, actually. See, for example, xUnit.net: it recommends using the constructor as the test case initializer. Being "just a structure provided by the language" is not a small matter; anyone writing any kind of framework (test frameworks included) should not try to reinvent the wheel, and make use of well defined standards instead (like, you know, using constructors for initializing stuff, and so on).Aid
It's amazing how Microsoft can write a whole web page and not give the basic information you gave in this simple straight forward answer. Good job.Cotyledon
@Cotyledon Sometimes they don't give all the info because it is considered an implementation detail that might change in the future. That being said, it may not be wise to write code that depends on an undocumented behavior. This is why I favor [TestInitialize] attribute instead of constructors.Eury
Should a test class be sealed or abstract ? or even staticChane
P
26

Here are some advantages I've found with TestInitialize.

  • Some environmental variables (e.g. TestContext) are not accessible until after the test class is instantiated.
  • Can require implementation with derived class by marking a base TestInitialize method abstract.
  • Can easily override a base TestInitialize method and determine whether to call the base impl before the derived impl, after, or at all. In contrast, if you derive a test class from a base test class, in the case of a parameterless constructor, the base ctor will be called whether you intended it to or not.
  • Its explicit definition makes the intentions clear, and complements the TestCleanup method. You might argue that you can create a destructor for every constructor, but it's not guaranteed that MS Test will handle destructors as you'd expect.
Prizefight answered 31/12, 2011 at 17:7 Comment(1)
I found that MSTest does run a public void Dispose method after each test run. It must be public, and it doesn't matter you implement IDisposable: you cannot explicitly implement the interface.Wallacewallach
H
15

The main advantage of using either TestInitialize() or ClassInitialize() rather than the test class instance or static constructors is its explicit nature. It clearly communicates that you are doing some setup prior to your tests. Doing this consistently should improve maintainability in the long run.

Holography answered 11/2, 2011 at 14:22 Comment(1)
I disagree. What could clearer than a constructor? Sure, you have to know you get a new test class instance for each test, but that's something you really should know anyway. And it's not like TestInitialize is such an obvious name that can't cause any confusion: #23000316.Tullius
T
7

I say use the constructor unless you need TestContext.

  1. If you can keep things simple, why not. A constructor is simpler than a magical attribute.
  2. You can use readonly which is a big thing in test initialization where you want to prepare stuff for the tests that they're not supposed to change (ideally the stuff you prepare would be immutable too).
Tullius answered 23/7, 2017 at 21:46 Comment(1)
+1 for the readonly. I just deleted my question about it when i saw your answer. But i usually would think in the other way: i use the TestInitialize unless i want a readonly attribute, since it looks a better "language" fit in test context. But it is just a design decision. I am ok with both approachs.Teenager
E
5

With async there is another reason (that did not exist when this question was asked) for [TestInitialize]. It allows you to do async operations to setup (eg. load a file) which is not possible in the constructor:

        private string approver;        

        [TestInitialize]
        public async Task Initialize()
        {
            approver = await File.ReadAllTextAsync("approver.json");
        }
Eugenol answered 13/6, 2019 at 9:21 Comment(0)
D
4

I prefer to use the [TestInitialize] method to perform instantiation of the object being tested and it's parameters. I only perform work in the constructor if it is necessary to instantiate a testing base class (which is usually where I create or refresh repositories, etc). This helps me keep the test framework code and test code separate logically and physically.

Dichromate answered 12/1, 2009 at 21:6 Comment(0)
G
3

This question is also asked (later) at What’s the difference between using the constructor in VS Testing framework vs. TestInitialize() attribute?

FWIW I assume by "class constructor" you mean the instance constructor (not the static constructor).

I believe the same question you are asking could equally be asked about the static constructor vs. ClassInitialize...

Galbraith answered 25/6, 2010 at 19:23 Comment(0)
C
2

The object you test doesn't need to be instantiated in the [TestInitialize] method. You can test the constructor of your object in a test method [Test].

Object in the [TestInitialize] can be to setup your persistance storage or to prepare value that the object tested will used in the tests.

Chiou answered 2/12, 2008 at 16:18 Comment(2)
I know this. You can instantiate the object inline with the declaration if possible. My question is what do you do in either of those places. And why?Dichromate
You didn't answer that question in your answer. Read the question again.Analisaanalise
R
0

It depends on the scenario. If you have a test class, and for some weird reason if you need to create instance of it on another test class, you will need to use constructor.

Otherwise test initialize more fits in the concept. Firstly, same reasons written above, second MS can introduce more features on that attribute and you will benefit them, with constructor you will stuck into it.

Renunciation answered 10/3, 2017 at 15:38 Comment(0)
B
0

I hope somebody still needs that. This is my solution, how to unit test class constructor. I am unit testing class service and throwing an exception if debuggingService is null.

DebuggingStepTests class constructor

private readonly IDebuggingService debuggingService;

public string StepName { get; set; }

public DebuggingStep(IDebuggingService _debuggingService)
{
    _log.Starting();
    StepName = "DebuggingStep";

    debuggingService = _debuggingService 
        ?? throw new ArgumentException("DebuggingStep init failure due to => IDebuggingService null");
}

UnitTests looks like this

    [Fact]
public void TestDebuggingStepConstructorWhen_InitServiceIsNull_ResultArgumentException() 
{
    //Arrange
    var arrange = new Action(() => 
    {
        new DebuggingStep(null);
    });

    //Act

    //Arrange
    Assert.Throws<ArgumentException>(arrange);
}

And actual result: enter image description here

Hope this will be helpful for somebody

Bipartisan answered 27/12, 2019 at 16:15 Comment(0)
A
0

This is what the MS document says: The TestInitialize is similar to the class constructor but is usually more suitable for long or async initializations.

Affettuoso answered 25/8, 2023 at 17:38 Comment(1)
Posting a documentation does not answer the OP's question: what do you tend to do in each of these areas? Do you avoid performing certain activities in either? What is your reason: style, technical, superstition?Currish
M
0

MSTEST0020: Prefer constructors over TestInitialize methods

Answers it:

https://learn.microsoft.com/en-us/dotnet/core/testing/mstest-analyzers/mstest0020

Malefactor answered 25/4 at 15:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.