Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Иногда нужно выполнять модульные тесты в определенном порядке. В идеальном случае порядок выполнения модульных тестов не должен иметь значение. Мы настоятельно рекомендуем отказаться от упорядочивания модульных тестов. Но иногда без этого невозможно обойтись. Если вы столкнулись с такой ситуацией, эта статья поможет вам упорядочить выполнение тестов.
Примечание.
Порядок тестирования и параллелизация тестов являются отдельными проблемами. При указании порядка выполнения определяется последовательность запуска тестов, но если параллелизация включена, несколько тестов по-прежнему могут выполняться одновременно. Чтобы гарантировать, что тесты выполняются по одному в указанном порядке, необходимо также отключить параллелизацию.
Если вы предпочитаете просматривать исходный код, ознакомьтесь с примером репозитория order .NET Core unit test.
Совет
Помимо возможностей упорядочивания, описанных в этой статье, рассмотрите возможность создания пользовательских списков воспроизведения с помощью Visual Studio в качестве альтернативы.
Упорядочение по алфавиту
Примечание.
MSTest выполняет тесты последовательно в классе по умолчанию. Если вы настраиваете параллелизм с помощью <Parallelize> параметра в .runsettings файле, тесты между классами могут выполняться одновременно, а порядок влияет только на последовательность в каждом классе.
MSTest обнаруживает тесты в том же порядке, в котором они определены в тестовом классе.
При выполнении с помощью обозревателя тестов (в Visual Studio или в Visual Studio Code) тесты упорядочены в алфавитном порядке в зависимости от их имени теста.
При выполнении за пределами обозревателя тестов тесты выполняются в том порядке, в котором они определены в тестовом классе.
Примечание.
Тест с именем Test14 будет выполняться до Test2, несмотря на то, что число 2 меньше 14. Это связано с тем, что порядок имен тестов использует текстовое название теста.
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace MSTest.Project;
[TestClass]
public class ByAlphabeticalOrder
{
public static bool Test1Called;
public static bool Test2Called;
public static bool Test3Called;
[TestMethod]
public void Test2()
{
Test2Called = true;
Assert.IsTrue(Test1Called);
Assert.IsFalse(Test3Called);
}
[TestMethod]
public void Test1()
{
Test1Called = true;
Assert.IsFalse(Test2Called);
Assert.IsFalse(Test3Called);
}
[TestMethod]
public void Test3()
{
Test3Called = true;
Assert.IsTrue(Test1Called);
Assert.IsTrue(Test2Called);
}
}
Начиная с MSTest 3.6, новый параметр runettings позволяет выполнять тесты по именам тестов как в обозревателях тестов, так и в командной строке. Чтобы включить эту функцию, добавьте параметр в OrderTestsByNameInClass файл runsettings:
<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
<MSTest>
<OrderTestsByNameInClass>true</OrderTestsByNameInClass>
</MSTest>
</RunSettings>
Платформа тестирования xUnit предоставляет больше возможностей, а также более высокую точность и уровень контроля порядка выполнения тестов. Для управления порядком тестовых случаев для класса или коллекций тестов необходимо реализовать интерфейсы ITestCaseOrderer и ITestCollectionOrderer.
Примечание.
xUnit выполняет тестовые классы параллельно по умолчанию. Тесты в одном классе всегда выполняются последовательно, поэтому ITestCaseOrderer управляет последовательностью в этом классе. Чтобы отключить параллелизм во всех классах, примените [assembly: CollectionBehavior(DisableTestParallelization = true)] на уровне сборки, например, в AssemblyInfo.cs или любом исходном файле тестового проекта.
Упорядочение тестовых случае по алфавиту
Чтобы упорядочить тестовые случаи по имени метода, следует реализовать ITestCaseOrderer и предоставить механизм упорядочения.
using Xunit.Abstractions;
using Xunit.Sdk;
namespace XUnit.Project.Orderers;
public class AlphabeticalOrderer : ITestCaseOrderer
{
public IEnumerable<TTestCase> OrderTestCases<TTestCase>(
IEnumerable<TTestCase> testCases) where TTestCase : ITestCase =>
testCases.OrderBy(testCase => testCase.TestMethod.Method.Name);
}
Затем задайте порядок тестовых случаев в тестовом классе с помощью TestCaseOrdererAttribute.
using Xunit;
namespace XUnit.Project;
[TestCaseOrderer(
ordererTypeName: "XUnit.Project.Orderers.AlphabeticalOrderer",
ordererAssemblyName: "XUnit.Project")]
public class ByAlphabeticalOrder
{
public static bool Test1Called;
public static bool Test2Called;
public static bool Test3Called;
[Fact]
public void Test1()
{
Test1Called = true;
Assert.False(Test2Called);
Assert.False(Test3Called);
}
[Fact]
public void Test2()
{
Test2Called = true;
Assert.True(Test1Called);
Assert.False(Test3Called);
}
[Fact]
public void Test3()
{
Test3Called = true;
Assert.True(Test1Called);
Assert.True(Test2Called);
}
}
Упорядочение коллекций по алфавиту
Чтобы упорядочить коллекции по отображаемому имени, следует реализовать ITestCollectionOrderer и предоставить механизм упорядочения.
using Xunit;
using Xunit.Abstractions;
namespace XUnit.Project.Orderers;
public class DisplayNameOrderer : ITestCollectionOrderer
{
public IEnumerable<ITestCollection> OrderTestCollections(
IEnumerable<ITestCollection> testCollections) =>
testCollections.OrderBy(collection => collection.DisplayName);
}
Так как коллекции тестов могут выполняться параллельно, явным образом отключите параллелизацию тестов для коллекций с помощью CollectionBehaviorAttribute. Затем определите реализацию в TestCollectionOrdererAttribute.
using Xunit;
// Need to turn off test parallelization so we can validate the run order
[assembly: CollectionBehavior(DisableTestParallelization = true)]
[assembly: TestCollectionOrderer(
ordererTypeName: "XUnit.Project.Orderers.DisplayNameOrderer",
ordererAssemblyName: "XUnit.Project")]
namespace XUnit.Project;
[Collection("Xzy Test Collection")]
public class TestsInCollection1
{
public static bool Collection1Run;
[Fact]
public static void Test()
{
Assert.True(TestsInCollection2.Collection2Run); // Abc
Assert.True(TestsInCollection3.Collection3Run); // Mno
Assert.False(TestsInCollection1.Collection1Run); // Xyz
Collection1Run = true;
}
}
[Collection("Abc Test Collection")]
public class TestsInCollection2
{
public static bool Collection2Run;
[Fact]
public static void Test()
{
Assert.False(TestsInCollection2.Collection2Run); // Abc
Assert.False(TestsInCollection3.Collection3Run); // Mno
Assert.False(TestsInCollection1.Collection1Run); // Xyz
Collection2Run = true;
}
}
[Collection("Mno Test Collection")]
public class TestsInCollection3
{
public static bool Collection3Run;
[Fact]
public static void Test()
{
Assert.True(TestsInCollection2.Collection2Run); // Abc
Assert.False(TestsInCollection3.Collection3Run); // Mno
Assert.False(TestsInCollection1.Collection1Run); // Xyz
Collection3Run = true;
}
}
Упорядочение по настраиваемому атрибуту
Чтобы упорядочить тесты xUnit по пользовательским атрибутам, сначала нужен атрибут, на который можно положиться. Определите TestPriorityAttribute следующим образом:
namespace XUnit.Project.Attributes;
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class TestPriorityAttribute : Attribute
{
public int Priority { get; private set; }
public TestPriorityAttribute(int priority) => Priority = priority;
}
Далее рассмотрим следующую реализацию интерфейса PriorityOrdererITestCaseOrderer.
using Xunit.Abstractions;
using Xunit.Sdk;
using XUnit.Project.Attributes;
namespace XUnit.Project.Orderers;
public class PriorityOrderer : ITestCaseOrderer
{
public IEnumerable<TTestCase> OrderTestCases<TTestCase>(
IEnumerable<TTestCase> testCases) where TTestCase : ITestCase
{
string assemblyName = typeof(TestPriorityAttribute).AssemblyQualifiedName!;
var sortedMethods = new SortedDictionary<int, List<TTestCase>>();
foreach (TTestCase testCase in testCases)
{
int priority = testCase.TestMethod.Method
.GetCustomAttributes(assemblyName)
.FirstOrDefault()
?.GetNamedArgument<int>(nameof(TestPriorityAttribute.Priority)) ?? 0;
GetOrCreate(sortedMethods, priority).Add(testCase);
}
foreach (TTestCase testCase in
sortedMethods.Keys.SelectMany(
priority => sortedMethods[priority].OrderBy(
testCase => testCase.TestMethod.Method.Name)))
{
yield return testCase;
}
}
private static TValue GetOrCreate<TKey, TValue>(
IDictionary<TKey, TValue> dictionary, TKey key)
where TKey : struct
where TValue : new() =>
dictionary.TryGetValue(key, out TValue? result)
? result
: (dictionary[key] = new TValue());
}
Затем в тестовом классе установите порядок тестовых случаев с помощью TestCaseOrdererAttribute до PriorityOrderer.
using Xunit;
using XUnit.Project.Attributes;
namespace XUnit.Project;
[TestCaseOrderer(
ordererTypeName: "XUnit.Project.Orderers.PriorityOrderer",
ordererAssemblyName: "XUnit.Project")]
public class ByPriorityOrder
{
public static bool Test1Called;
public static bool Test2ACalled;
public static bool Test2BCalled;
public static bool Test3Called;
[Fact, TestPriority(5)]
public void Test3()
{
Test3Called = true;
Assert.True(Test1Called);
Assert.True(Test2ACalled);
Assert.True(Test2BCalled);
}
[Fact, TestPriority(0)]
public void Test2B()
{
Test2BCalled = true;
Assert.True(Test1Called);
Assert.True(Test2ACalled);
Assert.False(Test3Called);
}
[Fact]
public void Test2A()
{
Test2ACalled = true;
Assert.True(Test1Called);
Assert.False(Test2BCalled);
Assert.False(Test3Called);
}
[Fact, TestPriority(-5)]
public void Test1()
{
Test1Called = true;
Assert.False(Test2ACalled);
Assert.False(Test2BCalled);
Assert.False(Test3Called);
}
}
Упорядочение по приоритету
Примечание.
NUnit выполняет тесты последовательно в одном потоке по умолчанию. Если вы не применили [Parallelizable] атрибуты, атрибут [Order] сам по себе достаточно для обеспечения последовательного выполнения в указанной последовательности.
Чтобы явным образом упорядочить тесты, в NUnit можно использовать OrderAttribute. Тесты с этим атрибутом всегда выполняются раньше, чем остальные. Для определения порядка выполнения модульных тестов используется значение порядка.
using NUnit.Framework;
namespace NUnit.Project;
public class ByOrder
{
public static bool Test1Called;
public static bool Test2ACalled;
public static bool Test2BCalled;
public static bool Test3Called;
[Test, Order(5)]
public void Test1()
{
Test1Called = true;
Assert.That(Test2ACalled, Is.False);
Assert.That(Test2BCalled, Is.True);
Assert.That(Test3Called, Is.True);
}
[Test, Order(0)]
public void Test2B()
{
Test2BCalled = true;
Assert.That(Test1Called, Is.False);
Assert.That(Test2ACalled, Is.False);
Assert.That(Test3Called, Is.True);
}
[Test]
public void Test2A()
{
Test2ACalled = true;
Assert.That(Test1Called, Is.True);
Assert.That(Test2BCalled, Is.True);
Assert.That(Test3Called, Is.True);
}
[Test, Order(-5)]
public void Test3()
{
Test3Called = true;
Assert.That(Test1Called, Is.False);
Assert.That(Test2ACalled, Is.False);
Assert.That(Test2BCalled, Is.False);
}
}