包含条件条件的单元测试方法的最佳实践

时间:2008-12-19 13:22:55

标签: unit-testing conditional

我是单元测试的新手,目前在查找测试包含分支的方法的正确方法方面存在问题。

我创建了一个小的演示方法,我希望可以用来解释这个问题。

public void ExportAccounts()
{
     int emptyAccounts = 0;
     int nonEmptyAccounts = 0;
     int errorous = 0;
     string outputPath = this.GetOutputPath();
     Account[] accounts = this.MyWebserviceAdapter.GetAccounts();
     foreach(Account account in accounts)
     {
        try 
        {
          if(account.Amount > 0)
          {
               this.ExportNonEmpty(outputPath, account);
               nonEmptyAccounts++;
          } else {
               this.ExportEmptyAccount(outputPath, account);
               emptyAccounts++;
          }
        } catch(Exception e) {
          logger.error(e);
          errorous++;
        }
     }
     logger.debug(string.Format("{0} empty / {1} non empty / {2} errorous", emptyAccounts, nonEmptyAccounts, errorous));
}

我可以模拟MyWebserviceAdapter来返回预定义的帐户列表。我应该在同一个测试中输入一个空的和非空的帐户列表,还是应该进行单独的测试?

我的ExportNonEmpty()和ExportEmpty()方法也是私有的,但是会将文件写入文件系统。我应该提供模拟FileProvider,以便不触及文件系统吗?

我应该公开ExportNonEmpty()和ExportEmpty()以便能够单独测试它们吗?这些方法还包含一些if-then-else语句,可以抛出异常等等。

我发现是否为每个代码路径创建了一个测试,我将代码从一个测试复制到另一个测试 - 生成模拟等等..不是有点奇怪吗?

我应该将计数器变量公开为out变量,以便在调用方法后验证它们吗?

this.GetOUtputPath()通过ConfigurationManager从配置文件中获取值是静态的。我应该a)通过为testt下的类创建部分模拟并覆盖GetOutputPath方法或b)创建我自己的可以模拟出来的ConfigurationAdapter来模拟这个吗?

我正在使用nunit和Rhino Mocks。

2 个答案:

答案 0 :(得分:1)

我会用几个测试向量来测试它:all-empty,all-non-empty,以及以空和非空为主的混合开始和结束。

至于验证结果:暴露和测试计数器将给出一个“白盒子”测试,其中测试知道对象的内部状态,这给出了更彻底的测试,但是更难以改变以后的实施。 (如果更改实现,即使效果相同,测试也可能会失败。)

我的偏好通常是测试“黑匣子”,并且只测试操作的外部可观察后果。然后,如果公开的功能仍然相同,您可以更改内部结构和回归测试。但是,这可能需要更多的模拟编码。

对于java,有很多库可以帮助你构建模拟对象,我不知道.net,但我会假设情况一样。

答案 1 :(得分:1)

针对每种情况单独进行测试。我假设您信任foreach迭代包含两者的循环。我会。据推测,您正在测试您在其他地方嘲笑的方法,以确保它在存在时正确返回两种类型的帐户。

您可以通过将常用代码提取到测试类中的方法甚至TestHelper类中来重构,而不是复制代码。如果需要,可以对它们进行参数化,使它们通常可用。

您应该可以通过为被测试的类添加一个访问器来测试您的私有方法。如果您在私有方法中使用“创建单元测试”右键单击菜单项,或者只是将私有方法添加为一个,以便在为整个类添加时创建单元测试,则会自动添加一个。对于测试,ExportAccounts只使用您知道的数据并且不会抛出异常,以便您可以测试直线逻辑和异常处理。

我不会公开方法变量。在方法之外不需要它们。但是,您应该模拟记录器以确保使用预期参数调用它。

创建ConfigurationAdapter(或Wrapper)并将其注入到类中以删除对静态类的依赖性。模拟适配器或提供假实现,您的选择。无论如何,删除依赖是一个很好的模式。我不想在被测试的班级中嘲笑或存在任何东西。

编辑:有关单元测试的基本阅读,我建议Pragmatic Unit Testing(C#版本)和Code Complete中的单元测试章节。您可能还想选择Test Driven Development in .Net,但其他更为通用。