2011-06-22 61 views
0

对不起,很长的文章...单元测试“结构”的方法?

虽然被引入到棕色领域的项目,我怀疑某些单元测试和思考。假设你有一个repostory类,包装一个存储过程并在开发人员指南中包含一些特定的准则(规则),描述应该如何构造这个类。类可能看起来像以下:现在

public class PersonRepository 
{ 
public PersonCollection FindPersonsByNameAndCity(string personName, string cityName) 
{ 
    using (new SomeProfiler("someKey")) 
    { 
     var sp = Ioc.Resolve<IPersonStoredProcedure>(); 

     sp.addNameArguement(personName); 
     sp.addCityArguement(cityName); 

     return sp.invoke(); 
    } 
} } 

,我当然会写一些集成测试,测试的SP可以被调用,并且该行为是按预期。然而,我会写单元测试断言:

  • 构造用于与所述输入参数“someKey” SomeProfiler称为
  • PersonStoredProcedure的构造被称为
  • 对所存储的过程中的addNameArgument方法被称为与参数PERSONNAME
  • 在存储过程addCityArgument方法称为使用参数的cityName
  • 调用方法被称为所存储的过程 -

如果是这样,我可能会测试一个方法的整个结构,除了行为。我最初的想法是,它是矫枉过正。但是,关于团队实施的编码实践,这些测试确保了统一和“正确”的结构,并且下一层被正确调用(从DAL到DB,BLL到DAL等)。

在我的情况下,这些类型的测试是针对应用程序的每一层执行的。

后续问题 - SomeProfiler类的使用有点像传统给我的感觉 - 而是为此创建显式测试,是否可以通过使用静态代码分析或unittest + reflection创建约定样式的测试?

在此先感谢。

回答

0

我认为你最初的想法是正确的 - 这是一种矫枉过正。尽管您可以使用反射来确保该类具有您期望的方法,但我不确定要如何测试它。

也许应该使用FxCop/StyleCop或nDepend之类的工具来代替单元测试,以确保特定程序集/ dll中的所有类都具有这些属性。

既然我是“只编写你需要的代码”的信徒,为什么要测试一个方法的存在,要么你在代码中的某个地方使用它,并且你可以测试特定的情况,或者你不需要 - 所以它是无关紧要的。

+0

我会标记为答案您的回复。希望对测试商业价值与代码结构进行更多的讨论。但是我对下一个代码审查有一点点意见。 – jaspernygaard

0

单元测试应该关注行为,而不是实现。因此编写一个测试来验证某些参数是否设置或通过并不会为您的测试策略增加太多价值。

由于提供的示例似乎与您的数据库进行通信,因此它不能真正被视为“单元测试”,因为它必须与具有其他设置和前提条件的物理依赖项进行通信,例如环境的可用性,数据库模式,现有数据,存储过程等。您编写的任何测试实际上也都验证这些前提条件。

在目前的情况下,对这些类型的测试最好的办法是测试类提供的行为 - 调用存储库中的方法,然后验证结果是否符合您的预期。但是,您会突然意识到这里存在隐藏成本 - 数据库在测试运行之间保持状态,并且您需要额外的设置或拆除逻辑以确保数据库处于已知状态。

虽然我意识到这个问题的目的是测试一个“黑匣子”,但看起来很明显在你的API中有一些隐藏的魔法。解决众所周知的状态问题的首选方法是使用内存数据库,该数据库的作用范围是当前的测试,它将我与环境考虑因素隔离,并使我能够对我的集成测试进行并行化。我敢打赌,在目前的设计下,没有“接缝”以编程方式引入数据库配置,因此你“陷入了困境”。以我的经验,魔法伤害。

然而,现有的设计略有变化解决了这个问题,“神奇”消失:

public class PersonRepository : IPersonRepository 
{ 
     private ConnectionManager _mgr; 

     public PersonRepository(ConnectionManager mgr) 
     { 
     _mgr = mgr; 
     } 

     public PersonCollection FindPersonsByNameAndCity(string personName, string cityName) 
     { 
      using (var p = _mgr.CreateProfiler("somekey")) 
      { 
       var sp = new PersonStoredProcedure(p); 

       sp.addArguement("name", personName); 
       sp.addArguement("city", cityName); 

       return sp.invoke(); 
      } 
     } 
} 
+0

虽然我完全同意你关于解耦代码和单元集成测试的价值,但是我的意图并不在于这篇文章,最重要的是测试用例。假设我们使用TypeMock或者StructureMap来解耦依赖关系。我会稍微改变示例代码... – jaspernygaard