Do you use TestInitialize or the test class constructor to prepare each test? and why?
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;
}
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.
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.