Выполнять юнит-тесты последовательно (а не параллельно) - PullRequest
61 голосов
/ 11 сентября 2009

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

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

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

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

Ответы [ 8 ]

71 голосов
/ 19 января 2016

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

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

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

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

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

или вообще без распараллеливания:

[assembly: CollectionBehavior(DisableTestParallelization = true)]

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

50 голосов
/ 08 ноября 2017

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

В 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");
        }
    }
}

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

42 голосов
/ 25 июля 2016

Для проектов .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" ]
  }
}
13 голосов
/ 11 июля 2017

Для проектов .NET Core вы можете настроить xUnit с файлом 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 голосов
/ 13 марта 2015

Вы можете использовать Плейлист

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

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

enter image description here

5 голосов
/ 11 сентября 2009

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

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

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

3 голосов
/ 11 сентября 2009

Может быть, вы можете использовать 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.");
  ...
}

Дайте мне знать, работает ли он.

0 голосов
/ 06 апреля 2019

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

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


      public class TestClass1 : SequentialTest
      {
      ...
      }

      public class TestClass2 : SequentialTest
      {
      ...
      }
    }
...