Ескертпе
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Жүйеге кіруді немесе каталогтарды өзгертуді байқап көруге болады.
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Каталогтарды өзгертуді байқап көруге болады.
Иногда нужно выполнять модульные тесты в определенном порядке. В идеальном случае порядок выполнения модульных тестов не должен иметь значение. Мы настоятельно рекомендуем отказаться от упорядочивания модульных тестов. Но иногда без этого невозможно обойтись. Если вы столкнулись с такой ситуацией, эта статья поможет вам упорядочить выполнение тестов.
Примечание.
Порядок тестирования и параллелизация тестов являются отдельными проблемами. При указании порядка выполнения определяется последовательность запуска тестов, но если параллелизация включена, несколько тестов по-прежнему могут выполняться одновременно. Чтобы гарантировать, что тесты выполняются по одному в указанном порядке, необходимо также отключить параллелизацию.
Если вы предпочитаете просматривать исходный код, ознакомьтесь с примером репозитория 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);
}
}