【问题标题】:Unit Test Fails due to uninitialized static property由于未初始化的静态属性,单元测试失败
【发布时间】:2016-09-28 20:34:01
【问题描述】:

我在静态类中有一个静态方法在单元测试中失败。问题是它需要来自不同静态类的公共静态属性的值。单元测试中属性的值始终为 null,因为源类未初始化。两个静态类都在同一个项目中。如果我直接执行该方法,我会得到想要的结果,但我无法使用测试。

静态A类:

static class App {

  private static string appDir;

  public static string AppDir => appDir;

  [STAThread]
  static void Main() {

    appDir = AppDomain.CurrentDomain.BaseDirectory;

    DbEng.PutIdbVal("x", "Y");  // Method under test - Works here

  }
}

静态 B 类:

public static class DbEng {

  private static SQLiteConnection idbCn = new SQLiteConnection("Data Source=" + App.AppDir + "gcg.idb"); // App.AppDir is valid when not testing, is null when testing.

  public static void PutIdbVal(string key, string value) {

  using (var cmd = new SQLiteCommand("INSERT INTO tKv (Key, Value) VALUES (@key, @value)", idbCn)) {
      cmd.Parameters.Add(new SQLiteParameter("@key", key));
      cmd.Parameters.Add(new SQLiteParameter("@value", value));
      idbCn.Open();
      cmd.ExecuteNonQuery();
      idbCn.Close();
    }
  }
}

单元测试:

[TestClass]
public class DbEng_Tests {
  [TestMethod]
  public void PutIdbVal_Test() {

    string TestKey = "Test-Key";
    string TestValue = "Test - Value";

    DbEng.PutIdbVal(TestKey, TestValue);

  }
}

是否可以强制单元测试代码在调用静态类B中的方法之前初始化静态类A?

【问题讨论】:

  • 你确定是因为静态类没有初始化吗? AppDomain 基目录更可能为 null,因为您正在单元测试中运行
  • 您的代码依赖于静态变量的初始化顺序。由于这种顺序不是定义的行为,因此您观察到的行为非常好(不是您想要的,但也很好)。
  • 由于您使用的是MSTest,您可以使用[DeploymentItem] 复制一个版本的gcg.idb 文件进行测试。见MSTest copy file to test run folder。虽然我更喜欢测试特定的配置文件...
  • @CoolBots:我有“使用静态 [namespace].App;”在 B 类中,因此添加类前缀是多余的。我没有在示例中包含它 - 所以我的错。
  • @pro3carp3 啊,我没想到using 声明!我根据您的问题认为这不是问题,我只是认为在问题发布期间丢失了一些东西。很高兴这一切都解决了:)

标签: c# visual-studio unit-testing


【解决方案1】:

静态类在首次使用该类的任何静态成员之前在首次访问时进行初始化。单元测试代码也不例外。

要直接回答您的问题,可以强制单元测试代码在调用静态类 B 中的方法之前初始化静态类 A - 为此,您只需要访问类 A 的任何公共静态成员:

string appDir = App.AppDir;

但是,这可能不是您的代码有什么问题,因为您在 B 类中访问 App.AppDir(如果您接受我对您的问题的编辑,最初只有 AppDir),应该正确初始化它.

A 类的变量appDirstatic void Main() 中初始化,它不会在单元测试中运行。您应该添加一个静态构造函数:

static App()
{
     appDir = AppDomain.CurrentDomain.BaseDirectory;
}

【讨论】:

  • 请注意,“第一次访问”是轻描淡写的错误信息。现实更加严酷 - C# spec - “在第一次使用该类的静态字段之前在依赖于实现的时间执行”(或 SO question)。
  • @AlexeiLevenkov 当然,根据规范不容易确定确切的时间,但是您是否同意初始化发生在使用之前?这样说公平吗?
  • 是的,很好……不幸的是,它使 OP 的代码不确定,因此失败是该代码的预期结果,而不是错误(必须更改代码以避免这种不确定性)。
  • @AlexeiLevenkov 添加一个静态构造函数并将appDir 的初始化从static void Main() 移入其中,因为构造函数将在首次使用之前运行 App.AppDir,你同意吗?
  • 似是而非。老实说,我不知道——这对于普通人来说是一种复杂的方式来了解和依赖。我一开始不会写这样的代码,也不会让我周围的任何人这样做。
【解决方案2】:

只需更改您的类App 以绕过静态字段并将目录放在静态属性getter 中。字段 appDir 很可能尚未设置,因为在调用 idbCn 上的静态字段初始化程序之前未调用 Main()

static class App {

  public static string AppDir => AppDomain.CurrentDomain.BaseDirectory;

  [STAThread]
  static void Main() {

    DbEng.PutIdbVal("x", "Y");  // Method under test - Works here

  }
}

【讨论】:

  • 我接受了 CoolBot 的回答,因为它包含解决方案,并且该帖子是第一位的,但是这个答案是正确且简洁的。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-12-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多