Понимание MSTest TestContext

Используя MSTest, мне нужно было получить имя текущего теста из метода [TestInitialize]. Вы можете получить это из свойства TestContext.TestName.

Я обнаружил неожиданное различие в поведении между статическим TestContext который передается методу [ClassInitialize] и тем, который объявлен как открытое свойство (и устанавливается [ClassInitialize] теста).

Рассмотрим следующий код:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace TestContext.Tests
{
    [TestClass]
    public class UnitTest1
    {
        public TestContext TestContext { get; set; }

        private static TestContext _testContext;

        [ClassInitialize]
        public static void SetupTests(TestContext testContext)
        {
            _testContext = testContext;
        }

        [TestInitialize]
        public void SetupTest()
        {
            Console.WriteLine(
                "TestContext.TestName='{0}'  static _testContext.TestName='{1}'",
                TestContext.TestName,
                _testContext.TestName);
        }

        [TestMethod] public void TestMethod1() { Assert.IsTrue(true); }

        [TestMethod] public void TestMethod2() { Assert.IsTrue(true); }

        [TestMethod] public void TestMethod3() { Assert.IsTrue(true); }
    }
}

Это приводит к выводу следующего (скопированного из выходных данных тестера Resharper в VS2013):

TestContext.TestName='TestMethod1'  static _testContext.TestName='TestMethod1'
TestContext.TestName='TestMethod2'  static _testContext.TestName='TestMethod1'
TestContext.TestName='TestMethod3'  static _testContext.TestName='TestMethod1'

Ранее я предполагал, что два экземпляра TestContext будут эквивалентны, но, очевидно, это не так.

  • public TestContext свойство public TestContext ведет себя так, как я ожидаю
  • private static TestContext значение private static TestContext которое передается методу [ClassInitialize] не имеет. Поскольку TestContext имеет свойства, которые относятся к TestContext тесту, эта реализация кажется вводящей в заблуждение и нарушенной

Есть ли сценарий, в котором вы бы предпочли использовать TestContext переданный методу [ClassInitialize], или его лучше игнорировать и никогда не использовать?

Ответ 1

Поскольку [ClassInitialize] вызывается только в начале, имя теста - TestMethod1. Это устарело после первого пробного запуска.

TestContext устанавливается для каждого метода и поэтому имеет текущее имя теста.

Да, это немного глупо.

Ответ 2

Метод

[ClassInitialize]
public static void SetupTests(TestContext testContext) { }

вызывается перед установкой свойства TestContext. Поэтому, если вам нужен контекст в SetupTests, этот параметр полезен. В противном случае используйте свойство TestContext, которое устанавливается перед каждым

[TestInitialize]
public void SetupTest() { }

Ответ 3

Сценарий: контекст для каждого теста.

Применяется к Visual Studio 2017 со следующими библиотеками:

  • Microsoft.VisualStudio.TestPlatform.TestFramework
  • Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions

Образец кода:

    [TestClass]
    public class MyTestClass
    {

        public TestContext TestContext { get; set; }

        /// <summary>
        /// Run before each UnitTest to provide additional contextual information.
        /// TestContext reinitialized before each test, no need to clean up after each test.
        /// </summary>
        [TestInitialize]
        public void SetupTest()
        {
            TestContext.Properties.Add("MyKey", "My value ...");

            switch (TestContext.TestName)
            {
                case "MyTestMethod2":
                    TestContext.Properties["MyKey2"] = "My value 2 ...";
                    break;
            }

        }

        [TestMethod]
        public void MyTestMethod()
        {
            // Usage:
            // TestContext.Properties["MyKey"].ToString()
        }   

        [TestMethod]
        public void MyTestMethod2()
        {
            // Usage:
            // TestContext.Properties["MyKey"].ToString()

            // also has:
            // TestContext.Properties["MyKey2"].ToString()
        }

    }

Ответ 4

Если вы хотите передать свои объекты, созданные в методе [ClassInitialize] (или [AssemblyInitialize]), в методы очистки и ваши тесты, вы должны сохранить его контекст инициализации в отдельной статической переменной, помимо обычного TestContext. Только так вы сможете получить его позже в своем коде.

public TestContext TestContext { get; set; } // regular test context
private static TestContext ClassTestContext { get; set; } // global class test context

[ClassInitialize]
public static void ClassInit(TestContext context)
{
        ClassTestContext = context;
        context.Properties["myobj"] = <Some Class Level Object>;
}

[ClassCleanup]
public static void ClassCleanup()
{
    object myobj = (object)ClassTestContext.Properties["myobj"];
}

[TestMethod]
public void Test()
{
    string testname = (string)TestContext.Properties["TestName"] // object from regular context
    object myobj = (object)ClassTestContext.Properties["myobj"]; // object from global class context
}

Платформа MSTest не сохраняет объекты контекста, переданные методу [ClassInitialize]/[AssemblyInitialize], поэтому после возврата они будут потеряны навсегда, если вы явно не сохраните их.