Подтвердить что ты не робот

Выполнять единичные тесты поочередно (а не параллельно)

Я пытаюсь использовать unit test механизм управления хостом WCF, который я написал. Двигатель в основном создает экземпляры ServiceHost "на лету" на основе конфигурации. Это позволяет нам динамически перенастраивать, какие сервисы доступны, не принося их всех и перезапускать при каждом добавлении новой службы или удалении старого.

Я столкнулся с трудностями в модульном тестировании этого механизма управления хостом, однако, из-за того, как работает ServiceHost. Если ServiceHost уже создан, открыт и еще не закрыт для конкретной конечной точки, другой ServiceHost для одной и той же конечной точки не может быть создан, что приведет к исключению. Из-за того, что современные платформы тестирования модулей распараллеливают выполнение своих тестов, у меня нет эффективного способа unit test этого фрагмента кода.

Я использовал xUnit.NET, надеясь, что из-за его расширяемости я мог бы найти способ заставить его запускать тесты поочередно. Однако мне не повезло. Я надеюсь, что кто-то здесь, на SO, столкнулся с подобной проблемой и знает, как получить модульные тесты для серийного запуска.

ПРИМЕЧАНИЕ: ServiceHost - это класс WCF, написанный Microsoft. У меня нет возможности изменить его поведение. Хостинг каждой конечной точки обслуживания только один раз также является правильным поведением... однако, это не особенно способствует единичному тестированию.

4b9b3361

Ответ 1

Как указано выше, все хорошие модульные тесты должны быть на 100% изолированы. Использование общего состояния (например, в зависимости от свойства static, которое изменяется каждым тестом) считается плохой практикой.

Сказав это, ваш вопрос о выполнении тестов xUnit в последовательности имеет ответ! Я столкнулся с той же проблемой, потому что моя система использует статический сервисный локатор (который не идеален).

По умолчанию xUnit 2.x выполняет все тесты параллельно. Это можно изменить для каждой сборки, указав CollectionBehavior в вашем AssemblyInfo.cs в тестовом проекте.

Для разделения на сборку используйте:

using Xunit;
[assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)]

или при отсутствии распараллеливания при любом использовании:

[assembly: CollectionBehavior(DisableTestParallelization = true)]

Последний, вероятно, тот, который вы хотите. Более подробную информацию о параллелизации и конфигурации можно найти в документации xUnit.

Ответ 2

Каждый класс тестов является уникальным набором тестов, и тесты в нем будут выполняться последовательно, поэтому, если вы поместите все свои тесты в одну коллекцию, он будет выполняться последовательно.

В xUnit вы можете внести следующие изменения, чтобы добиться этого:

Следующее будет работать параллельно:

namespace IntegrationTests
{
    public class Class1
    {
        [Fact]
        public void Test1()
        {
            Console.WriteLine("Test1 called");
        }

        [Fact]
        public void Test2()
        {
            Console.WriteLine("Test2 called");
        }
    }

    public class Class2
    {
        [Fact]
        public void Test3()
        {
            Console.WriteLine("Test3 called");
        }

        [Fact]
        public void Test4()
        {
            Console.WriteLine("Test4 called");
        }
    }
}

Чтобы сделать его последовательным, вам просто нужно поместить оба тестовых класса в одну коллекцию:

namespace IntegrationTests
{
    [Collection("Sequential")]
    public class Class1
    {
        [Fact]
        public void Test1()
        {
            Console.WriteLine("Test1 called");
        }

        [Fact]
        public void Test2()
        {
            Console.WriteLine("Test2 called");
        }
    }

    [Collection("Sequential")]
    public class Class2
    {
        [Fact]
        public void Test3()
        {
            Console.WriteLine("Test3 called");
        }

        [Fact]
        public void Test4()
        {
            Console.WriteLine("Test4 called");
        }
    }
}

Для получения дополнительной информации вы можете обратиться по этой ссылке

Ответ 3

Для проектов .NET Core создайте xunit.runner.json с:

{
  "parallelizeAssembly": false,
  "parallelizeTestCollections": false
}

Кроме того, ваш csproj должен содержать

<ItemGroup>
  <None Update="xunit.runner.json"> 
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </None>
</ItemGroup>

Для старых проектов .Net Core ваш project.json должен содержать

"buildOptions": {
  "copyToOutput": {
    "include": [ "xunit.runner.json" ]
  }
}

Ответ 4

Для проектов .NET Core вы можете настроить xUnit с помощью файла xunit.runner.json, как xunit.runner.json в https://xunit.github.io/docs/configuring-with-json.html.

Параметр, который вам нужно изменить, чтобы остановить выполнение параллельного теста, - parallelizeTestCollections, который по умолчанию имеет значение true:

Задайте для этого параметра значение true если сборка желает параллельно выполнять тесты внутри этой сборки друг против друга.... Установите для этого параметра значение false чтобы отключить все распараллеливание внутри этой тестовой сборки.

Тип схемы JSON: логическое значение
Значение по умолчанию: true

Таким образом, минимальный xunit.runner.json для этой цели выглядит

{
    "parallelizeTestCollections": false
}

Как отмечено в документации, не забудьте включить этот файл в вашу сборку, либо:

  • Если для параметра "Копировать в выходной каталог" выбрано значение "Копировать" в свойствах файла в Visual Studio
  • Добавление

    <Content Include=".\xunit.runner.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
    

    в ваш файл .csproj, или

  • Добавление

    "buildOptions": {
      "copyToOutput": {
        "include": [ "xunit.runner.json" ]
      }
    }
    

    в ваш файл project.json

в зависимости от типа вашего проекта.

Наконец, в дополнение к вышесказанному, если вы используете Visual Studio, убедитесь, что вы случайно не нажали кнопку Run Tests In Parallel, что приведет к параллельному запуску тестов, даже если вы отключили параллелизацию в xunit.runner.json. Разработчики пользовательского интерфейса Microsoft хитро сделали эту кнопку немаркированной, трудно заметной и находящейся примерно в сантиметре от кнопки "Запустить все" в Test Explorer, просто чтобы максимально увеличить вероятность того, что вы нажмете ее по ошибке и не будете знать, почему ваши тесты вдруг терпят неудачу:

Screenshot with the button circled

Ответ 5

вы можете использовать Список воспроизведения

щелкните правой кнопкой мыши по методу тестирования → Добавить в плейлист → Новый плейлист

тогда вы можете указать порядок выполнения, по умолчанию, по мере добавления их в список воспроизведения, но вы можете изменить файл списка воспроизведения так, как хотите.

enter image description here

Ответ 6

Я не знаю подробностей, но похоже, что вы можете попробовать тестирование интеграции, а не модульное тестирование. Если вы можете изолировать зависимость от ServiceHost, это, скорее всего, упростит ваше тестирование (и быстрее). Так (например) вы можете проверить следующее независимо:

  • Класс чтения конфигурации
  • ServiceHost factory (возможно, как тест интеграции)
  • Класс двигателя, который принимает IServiceHostFactory и IConfiguration

Инструменты, которые помогут включить изоляционные (издевательские) фреймворки и (необязательно) инфраструктуру контейнеров IoC. См:

Ответ 7

Возможно, вы можете использовать Advanced Unit Testing. Он позволяет определить последовательность, в которой вы запускаете тест. Поэтому вам, возможно, придется создать новый файл cs для размещения этих тестов.

Здесь вы можете сгибать методы тестирования для работы в нужной последовательности.

[Test]
[Sequence(16)]
[Requires("POConstructor")]
[Requires("WorkOrderConstructor")]
public void ClosePO()
{
  po.Close();

  // one charge slip should be added to both work orders

  Assertion.Assert(wo1.ChargeSlipCount==1,
    "First work order: ChargeSlipCount not 1.");
  Assertion.Assert(wo2.ChargeSlipCount==1,
    "Second work order: ChargeSlipCount not 1.");
  ...
}

Сообщите мне, работает ли это.

Ответ 8

Я добавил атрибут [Collection ("Sequential")] в базовый класс:

    namespace IntegrationTests
    {
      [Collection("Sequential")]
      public class SequentialTest : IDisposable
      ...


      public class TestClass1 : SequentialTest
      {
      ...
      }

      public class TestClass2 : SequentialTest
      {
      ...
      }
    }