单元测试:program.cs Main

时间:2018-09-24 16:46:50

标签: c# testing main xunit

我的项目开始如下。我想创建一个单元测试来验证当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 :(得分:0)

在不更改任何代码的情况下,有两种方法可以做到这一点:

  • 实际上使用Process类运行该程序,并检查程序的退出代码(在Process.ExitCode属性中公开)。在这种情况下,类别为internalMainprivate的事实并不重要。

  • 使用反射保留Main方法并调用它,检查返回值。

如果这些都不是可口的,则可以将Main更改为internal并使用[InternalsVisibleTo]属性来使您的单元测试无需反射就可以调用该方法。

对于所有这3种情况,如果退出代码为0,则测试通过,如果退出代码为2,则测试失败(假设没有其他可能的退出代码)。

答案 1 :(得分:0)

在最佳情况下,您会将Log依赖项注入Main或要测试的任何类中。然后,您将测试在开发的任何情况下都可以调用Log方法。您还将注入配置和文件访问依赖关系,而不是紧密耦合,因此您不必实际在物理介质上进行真正的读/写访问来测试代码。就目前而言,您不能进行真实单元代码测试,因为您具有紧密耦合的外部依赖关系,因此您的代码在技术上正在进行集成测试。

我建议阅读Autofac DI container in console app。 (我与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%工作。 现在,当我想测试工作人员时,我可以从字面上测试任何东西: (我假设您想使用nSubstitutenUnit进行测试)

[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);
  }
}