Модульное тестирование: попробуйте оператор в program.cs Main - PullRequest
0 голосов
/ 24 сентября 2018

У меня есть проект, который начинается, как показано ниже.Я хочу создать модульный тест для проверки того, что происходит, когда либо appsetting.Environment.json не существует, либо файл DataSource:JsonPath не существует.

Я использую xUnit, но просто вхожу в него,

При таком сценарии он должен выбросить FileNotFoundException и выйти.

internal class Program
{
    private static int Main(string[] args)
    {
        public static IConfiguration Configuration { get; } = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ENVIRONMENT") ?? "Production"}.json", optional: true)
            .Build();

        try
        {
            Log.Information($"{DateTime.Now.ToLongTimeString()} Starting...");

            var jsonFileName = Configuration["DataSource:JsonPath"];
            if (!File.Exists(jsonFileName)) throw new FileNotFoundException(jsonFileName);

            //code if file exist...
        }
        catch (Exception ex)
        {
            Log.Fatal(ex, "Terminated unexpectedly");
            return 1;
        }
        finally
        {
            Log.CloseAndFlush();
        }
    }
}

Ответы [ 2 ]

0 голосов
/ 24 сентября 2018

В лучшем случае вы бы добавили Log зависимость в Main или в любой класс, который вы пытаетесь протестировать.Затем вы проверите, что ваши Log методы были вызваны при любых обстоятельствах, которые вы разработали.Вам также необходимо внедрить вашу зависимость «Конфигурация» и «Доступ к файлу» вместо того, чтобы быть тесно связанными, поэтому вам не нужно фактически иметь реальный доступ для чтения / записи на физическом носителе для проверки вашего кода.В существующем состоянии вы не можете выполнять тестирование реального модульного кода , поскольку у вас тесно связаны внешние зависимости, поэтому в вашем коде технически проводится интеграционное тестирование.

Я бы предложил прочитать Контейнер Autofac DI.в консольном приложении .(У меня нет аффилированного лица с Autofac , я просто предпочитаю его другим DI-фреймворкам. Многие так и делают, если вы решите их использовать).Если бы я реализовал это, это могло бы выглядеть примерно так:

internal class Program
{
    private static int Main(string[] args)
    {
      var container = CreateContainer();
      var worker = container.resolve<IWorker>();
      worker.Execute();
    }

}

internal class Worker : IWorker
{
    private IConfiguration _configuration;
    private IFileSystem _filesystem;
    private ILog _log;
    private IDateTimeFactory _datetimeFactory;

    public Worker(
      IConfiguration configuration,
      IFileSystem filesystem,
      ILog log,
      IDateTimeFactory _datetimeFactory)
    {
      _configuration = configuration;
      _filesystem = filesystem;
      _log = log;
      _datetimeFactory = datetimeFactory;
    }

    public void Execute()
    {
        try
        {
            _log.Information($"{_datetimeFactory.Now.ToLongTimeString()} Starting...");

            var jsonFileName = _configuration["DataSource:JsonPath"];
            if (!_filesystem.Exists(jsonFileName)) 
              throw new FileNotFoundException(jsonFileName);

            //code if file exist...
        }
        catch (Exception ex)
        {
            _log.Fatal(ex, "Terminated unexpectedly");
            return 1;
        }
        finally
        {
            _log.CloseAndFlush();
        }
    }
}

Ниже приведен общий пример, я не могу гарантировать, что он работает на 100%.Теперь, когда я хочу проверить работника, я могу буквально протестировать что угодно: (я предполагаю, что вы хотите протестировать, используя nSubstitute и nUnit )

[TestFixture]
public WorkerTests
{
  [Test]
  public void Execute_ConfigurationFileNotFound_LogsException()
  {
     var config = Substitute.For<IConfiguration>();
     var filesystem = Substitute.For<IFileSystem>();
     var log = Substitute.For<ILog>();
     var datetime = Substitute.For<IConfiIDateTimeFactory>();

     var worker = new Worker(
       config,
       filesystem,
       log,
       datetime)

     // setup the mock'd file system to always return false for Exists()
     // That is what we're testing
     filesystem.Exists(string.Empty).ReturnsForAnyArgs(false);

     worker.Execute();

     Assert.That(log.Received().Fatal(), Is.True);
  }
}
0 голосов
/ 24 сентября 2018

Без изменения какого-либо кода вы можете сделать это двумя способами:

  • Фактически запустите программу, используя класс Process, и проверьте выходкод вашей программы (отображается в свойстве Process.ExitCode).В этом случае тот факт, что класс равен internal, а Main равен private, не имеет значения.

  • Используйте отражение, чтобы овладеть методом Mainи вызовите его, проверяя возвращаемое значение.

Если ни один из них не является приемлемым, вы можете изменить Main на internal и использовать [InternalsVisibleTo] атрибут, позволяющий вашему модульному тесту вызывать этот метод без отражения.

Для всех 3 из этих случаев ваш тест проходит, если код выхода равен 0, и не проходит, если код выхода равен 2 (при условии, что других возможных вариантов нет).коды выхода).

...