LCJ
LCJ

Reputation: 22652

VS Team Test: Multiple Test Initialize Methods in Test Class

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?

Upvotes: 12

Views: 21293

Answers (5)

Tim Poindexter
Tim Poindexter

Reputation: 21

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.

Upvotes: 2

chaliasos
chaliasos

Reputation: 9783

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
    }
}

Upvotes: 25

Dave
Dave

Reputation: 3717

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
    }

Upvotes: 6

Sergey Berezovskiy
Sergey Berezovskiy

Reputation: 236218

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.

Upvotes: 12

Dave Lawrence
Dave Lawrence

Reputation: 3929

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

Upvotes: 3

Related Questions