Как выполнять определенные модульные тесты только при запуске в определенной операционной системе?

#c# #moq #xunit

Вопрос:

Приведен следующий пример помощника

 public static class RuntimeHelper
{
    public static bool IsLinux => RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
    public static bool IsMac => RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
    public static bool IsWindows => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
}
 

Есть несколько вопросов

  • Должен ли я вообще проводить модульное тестирование таких полей? (это может быть основано на мнении)
  • Есть ли способ создавать модульные тесты на основе операционной системы?

Примером реализации может быть

 public sealed class RuntimeHelperTests
{
    // IF RUNNING ON WINDOWS

    [Fact]
    public void TheOSShouldBeWindows()
    {
        Assert.True(RuntimeHelper.IsWindows);
    }

    [Fact]
    public void TheOSShouldNotBeLinux()
    {
        Assert.False(RuntimeHelper.IsLinux);
    }

    [Fact]
    public void TheOSShouldNotBeMac()
    {
        Assert.False(RuntimeHelper.IsMac);
    }

    // DO THIS FOR THE OTHER OPERATING SYSTEMS TOO
}
 

Но эти тесты проходят только при запуске на компьютере с Windows…

Комментарии:

1. Трудно ответить на этот вопрос без конкретики того, что RuntimeInformation есть. Но в конечном счете вы должны высмеивать любые внешние зависимости. Так что все, что говорит вам «это окна» и т. Д., Должно быть высмеяно

2. Кроме того, статика-это нет-нет с точки зрения модульного тестирования. Это делает чрезвычайно трудным любое издевательство. По существу избегайте использования статики, когда это возможно

3. @Liam: RuntimeInformation -это статический класс, предоставляемый .NET

Ответ №1:

Мы можем написать много слоев в нашей логике. На этот счет нет практического верхнего предела. Однако, независимо от того, сколько слоев мы пишем, снаружи всегда есть один слой, который должен взаимодействовать с реальным миром (например, монитор, сеть, операционная система, файловая система, устройство ввода-вывода,…)

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

В вашем случае RuntimeInformation это ваша не поддающаяся блокировке зависимость от реального мира. Это одна из главных проблем static при тестировании.

Вообще говоря, совет состоит в том, чтобы затем отделить бизнес-логику от внешней зависимости, чтобы вы могли протестировать бизнес-логику, обернув внешнюю зависимость в приятный насмешливый собственный слой. Вот мой ответ, уточняющий именно это.

Однако в вашем случае нет реальной бизнес-логики. Ваш RuntimeHelper уже является этой абстрактной оболочкой.

Для этого не имеет смысла писать тесты RuntimeHelper .

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

Как выполнять определенные модульные тесты только при запуске в определенной операционной системе?

Ты задаешь неправильный вопрос. Вместо того, чтобы писать разные тесты для работы на фактически разных ОС, вы должны писать одни и те же (поведенческие) тесты, но в каждом тесте насмешливо RuntimeHelper заявлять, что вы работаете в другой ОС.

Например:

 [Fact]
public void FilePathService_returns_correct_linux_path()
{
    var mockedRuntimeHelper = new MockedRuntimeHelper(OS.Linux);
    var filePathService = new FilePathService(mockedRuntimeHelper);

    var result = filePathService.GetConfigurationFolder();

    result.Should().Be("/path/to/config");
}

[Fact]
public void FilePathService_returns_correct_windows_path()
{
    var mockedRuntimeHelper = new MockedRuntimeHelper(OS.Windows);
    var filePathService = new FilePathService(mockedRuntimeHelper);

    var result = filePathService.GetConfigurationFolder();

    result.Should().Be(@"C:pathtoconfig");
}

[Fact]
public void FilePathService_returns_correct_mac_path()
{
    var mockedRuntimeHelper = new MockedRuntimeHelper(OS.Mac);
    var filePathService = new FilePathService(mockedRuntimeHelper);

    var result = filePathService.GetConfigurationFolder();

    result.Should().Be(@"whatever a Mac path is, I don't know");
}
 

Это позволяет протестировать все три поведения, выполняемые в любой операционной системе.

По-прежнему неплохо протестировать свой код на всех ОС, просто чтобы убедиться, что в вашей среде выполнения нет странных крайних случаев, но это не связано с желанием писать конкретные модульные тесты, которые следует запускать только на определенных операционных системах.

Комментарии:

1. Просто короткая заметка. ОП пометил это moq. Поэтому им не нужно на самом деле создавать MockedRuntimeHelper . Moq может сделать это за них var mockedRuntimeHelper = new Mock<IRuntimeHelper>(); , я бы также посоветовал вам отключить своих иждивенцев с помощью интерфейсов , чтобы облегчить этот процесс