12

I have unit test project called “MyClassTest” in TeamTest. This project has three TestMethods. Each method needs its own test initialization steps. But when I apply TestInitializeAttribute to three initialization methods, it says the attribute should not be used more than once. Then what should be the attribute to be used to initialize each test method in Visual Studio Team Test?

Reference:

  1. VS Team Test: .Net Unit Testing with Excel as Data Source: Adapter Failed

  2. How to create Startup and Cleanup script for Visual Studio Test Project?

  3. VS 2010 Load Tests Results with custom counters

  4. How to log unit test entry and leave in MSTest

  5. Can a unit test project load the target application's app.config file?

Community
  • 1
  • 1
LCJ
  • 22,196
  • 67
  • 260
  • 418

5 Answers5

24

According to MSDN the TestInitializeAttribute:

  • cannot be used more than once (AllowMultiple = false), and
  • cannot be inherited to create your own TestInitializeAttribute.

So, my suggestion is to create the Test Initialize Methods without the TestInitialize attribute. Then in the unique TestInitialize method check which is the current executed TestMethod and call the appropriate initialize method:

[TestClass]
public class UnitTest
{
    public TestContext TestContext { get; set; }

    [TestInitialize]
    public void Initialize()
    {
        switch (TestContext.TestName)
        {
            case "TestMethod1":
                this.IntializeTestMethod1();
                break;
            case "TestMethod2":
                this.IntializeTestMethod2();
                break;
            default:
                break;
        }
    }

    [TestMethod]
    public void TestMethod1()
    {
    }

    [TestMethod]
    public void TestMethod2()
    {
    }

    public void IntializeTestMethod1()
    {
        //Initialize Test Method 1
    }

    public void IntializeTestMethod2()
    {
        //Initialize Test Method 2
    }
}
abatishchev
  • 98,240
  • 88
  • 296
  • 433
chaliasos
  • 9,659
  • 7
  • 50
  • 87
  • 1
    Thanks. This seems feasible. Is it the standard practice that people follow? – LCJ May 22 '12 at 11:09
  • This is the practice my team follows. I don't know if there is another better way :) – chaliasos May 22 '12 at 11:42
  • 9
    `AllowMultiple = false` enforces single usage in the same element only... that means a single method cannot be marked with multiple `TestInitializeAttribute`. The rule that it cannot be used on different methods, has nothing to do with `AllowMultiple = false`. – Miguel Angelo Dec 29 '12 at 17:47
  • 2
    Though this works, it has the disadvantage that if the test names are refactored and the magic strings not updated, the tests would break. Why can't initialization be part of the tests "Arrange" part (i.e. in the test body or a private method if more than one test require the same initialization)? Cleanup can then be launched from a try..finally block in a test. I personally do this and it works well thus far. – Sudhanshu Mishra Dec 05 '15 at 22:20
12

If you have three test methods, and each method has its own initialization steps, then why are you moving initialization to method which will run before every test? Only benefit I see, is that nice switch block, which adds some lines to your source file. But it gives you drawback - looking on any of these test methods, you can't really tell in which context method will be executed. So, I use initialization method to setup only basic context, which is really used by all tests in fixture.

Just move context creation to arrange part of each method.

If you have several methods, which use common context, then just extract method, which will setup context for them, and call it at the arrange part. You also can split each context setup to several steps and reuse those steps (like it done in Given-When-Then tools like Specflow).

And, of course, creating different fixtures also option.

Community
  • 1
  • 1
Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
  • 1
    In the scope of load testing through integration tests, like in the case of load testing a WCF service layer, you want the test methods to be as clean as possible, because the test method execution time is what is going to be tracked. This is a clear case where moving all initialization logic to test initialize is the only way to go! – David Rodrigues Aug 08 '13 at 08:49
  • 1
    @DavidRodrigues not only. You can create separate TestFixture class for each feature you are testing. It will be very simple and it's SetUp method will not have any switch blocks. Also I'd go with something like SpecFlow for acceptance testing. There is nice Given methods which are nicely organized and can be reused. SpecFlow writes all steps execution times to output window – Sergey Berezovskiy Aug 08 '13 at 09:09
6

It's a bit of an old post, but I came up with the following which seems to work OK: First, define an attribute class:

[AttributeUsage(AttributeTargets.Method, Inherited = true)]
public class InitialiseWithAttribute : Attribute
{
    public string Id { get; private set; }

    public InitialiseWithAttribute(string id)
    {
        Id = id;
    }
}

then define an extension method in some convenient utilities class:

    public static bool IsInitialisedWith(this string testName, string value)
    {
        bool result = false;
        Type testClassType = new StackFrame(1).GetMethod().DeclaringType;
        MethodInfo methodInfo = testClassType.GetMethod(testName);
        if (methodInfo != null)
        {
            InitialiseWithAttribute initialiseWithAttribute =
                methodInfo.GetCustomAttribute<InitialiseWithAttribute>(true);
            if (initialiseWithAttribute != null)
            {
                result = initialiseWithAttribute.Id == value;
            }
        }
        return result;
    }

Now write your tests, thus:

    public TestContext TestContext {get; set;}
    [TestInitialize]
    public void TestInitialise()
    {
        if (TestContext.TestName.IsInitalisedWith("DoSomethingSpecial")
        {
             // ... Do something special
        }
        else
        {
             // ... Do something normal
        }
    }

    [TestMethod]
    [InitialiseWith("DoSomethingSpecial")]
    public void MySpecialTest()
    {
         // The test
    }
Dave
  • 3,429
  • 2
  • 26
  • 29
3

If they need three seperate inits; then they should probably be in three separate fixtures each with their own init!

Dave Lawrence
  • 3,843
  • 2
  • 21
  • 35
  • Schaliasos suggested a method above. That should be fine. Do you see any shortcomings? – LCJ May 22 '12 at 11:10
  • 2
    No.. not really. As long as the three tests are logically related. Me personally.. I'd split it out into three fixtures but that's a personal preference thing. The above looks fine. Are you required to do test cleanup after as well? Can the same approach be used? – Dave Lawrence May 22 '12 at 11:21
2

At my job we pass in an argument to TestInitialize method to determine how we want initialization to work.

public partial class CommonActions
{
   public void TestInitialize(bool adminTest)
   {
      try
      {
         if (adminTest)
         {
            //do stuff
         } 

We then have a standard initialization in class definition, which defaults to false.

[TestClass]
public class ProjectTestBase : FrameworkTestBase
{ 
  public CommonActions common { get; set; } = new CommonActions();

  [TestInitialize]
   public void TestInitialize() => common.TestInitialize(false);

Then in the Test cases themselves you can override the TestInitialize for any test you want.

[TestClass]
public class SetReportsInAdmin : ProjectTestBase
{
    [TestInitialize]
    public new void TestInitialize() => common.TestInitialize(true);

We use a Boolean to tell if Admin test, which needs to have extra overhead for setup. Take this and apply whatever variables you want in a way the gives you multiple initialization through the use of one method.