【问题标题】:Unit testing: try statement in program.cs Main单元测试:program.cs Main 中的 try 语句
【发布时间】:2019-02-28 05:41:42
【问题描述】:

我的项目如下所示。我想创建一个单元测试来验证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();
        }
    }
}

【问题讨论】:

  • 你的单元测试方法是什么样的?你到底面临什么问题?
  • 顺便说一句,你的 Main 方法不会抛出任何异常。如果您已经进入单元测试,那么您应该已经具备基本的 C# 知识才能看到这个...... ;-)
  • 我只是不知道从哪里开始,这可能是因为它在 Main 中让我感到困惑。 ***Elgonzo,因为有一个 !File.Exist 它确实会抛出新的异常并陷入困境,不确定你的意思
  • 那么您是否尝试过编写单元测试?我在这里没有看到任何证据。您的单元测试方法在哪里,包括 Arrange、Act、Assert?
  • @KaptainJ,我不是在谈论 File.Exist 方法,而是在谈论 Main 方法。 (即,像try { Program.Main(...); } catch(Exception ex) {...} 这样的东西永远不会捕捉到任何东西,因为没有什么可以捕捉到)

标签: c# testing main xunit


【解决方案1】:

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

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

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

如果这些都不合适,您可以将Main 更改为internal 并使用[InternalsVisibleTo] 属性使您的单元测试能够在不进行反射的情况下调用该方法。

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

【讨论】:

    【解决方案2】:

    在最好的情况下,您会将 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);
      }
    }
    

    【讨论】:

      猜你喜欢
      • 2015-06-15
      • 2016-08-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-06
      • 1970-01-01
      • 2020-01-18
      相关资源
      最近更新 更多