我正在用C#,NUnit和Rhino Mocks编写单元测试。以下是我正在测试的课程的相关部分:
public class ClassToBeTested { private IList<object> insertItems = new List<object>(); public bool OnSave(object entity, object id) { var auditable = entity as IAuditable; if (auditable != null) insertItems.Add(entity); return false; } }
我想在调用OnSave之后测试insertItems中的值:
[Test] public void OnSave_Adds_Object_To_InsertItems_Array() { Setup(); myClassToBeTested.OnSave(auditableObject, null); // Check auditableObject has been added to insertItems array }
最佳做法是什么?我考虑过将insertItems作为具有公共属性的属性添加,或将列表注入ClassToBeTested,但是不确定我是否出于测试目的而修改代码。
我已经阅读了许多有关测试私有方法和重构的文章,但这是一个如此简单的类,我想知道什么是最佳选择。
快速的答案是,您永远都不应从单元测试中访问非公开成员。它完全违背了拥有测试套件的目的,因为它会将您锁定在内部实现细节中,而您可能不想这样做。
答案越长,该怎么办?在这种情况下,重要的是要理解实现的原样(这就是TDD之所以如此强大的原因,因为我们使用测试来 指定 预期的行为,但是我感觉到您没有使用TDD)。
对于您的情况,想到的第一个问题是:“为什么将IAuditable对象添加到内部列表中?” 或者换句话说,“ 此实施的预期 外部可见 结果是什么?” 根据这些问题的答案, 这 就是您需要测试的内容。
如果将IAuditable对象添加到内部列表中是因为您以后想要将它们写入审核日志中(只是一个大胆的猜测),请调用写入日志的方法并验证是否已写入期望的数据。
如果您想将IAuditable对象添加到您的内部列表中是因为您想针对后来出现的某种约束收集证据,请尝试进行测试。
如果您添加的代码没有明显的原因,请再次删除它:)
重要的是测试 行为 而不是 实现 非常有益。它也是一种更健壮和可维护的测试形式。
不要害怕将被测系统(SUT)修改为更具可测试性。只要您的添加在您的领域中有意义并遵循面向对象的最佳实践,就不会有问题- 您将遵循开放式/封闭式原则。