我正在使用MS UnitTesting并试图找到我的第一次单元测试的方法。看起来我的所有单元测试都是从创建相同的几个对象开始的......
[TestMethod]
CanCreateOrder()
{
<create an order>
...
}
[TestMethod]
CanSetOrderDeliveryAddress()
{
<create an order>
<create an address>
order.DeliveryAddress = address;
...
}
[TestMethod]
CanDispatchAnOrder()
{
<create an order>
<create an address>
order.DeliveryAddress = address;
order.Dispatch();
...
}
...等
这是正常的,还是我的想法错了?我认为每个测试都应该是独立的,但是如果没有隐含地依赖于已经传递的Dispatch()
和CreateOrder
,那么如何测试SetDeliveryAddress
呢?
问题的第二部分,如果上面的方法看起来不错,我应该在我的测试项目中使用工厂或其他东西来实例化这些对象吗?我不确定测试项目是否应该只包含测试类/方法,或者也可以添加一堆帮助程序。
答案 0 :(得分:8)
你似乎走在了正确的轨道上。在许多单元测试中,您需要设置要测试的对象的状态,以便能够测试特定的行为部分。 当您测试的bahaviour需要类似设置时,它将有助于在类中对测试进行分组,并使用[TestInitialize]方法来减少该设置的重复。
例如:
[TestClass]
public class WhenReadyToDispatch{
private Order order;
[TestInitialize]
public void Initialize
{
order = <create an order>
order.DeliveryAddress = <create an address>
}
[TestMethod]
CanChangeOrderDeliveryAddress()
{
order.DeliveryAddress = address;
}
[TestMethod]
CanDispatchAnOrder()
{
order.Dispatch();
}
}
在测试项目中安装辅助类是很好的 - 您的目标应该是将测试代码作为生产代码的良好因素。
答案 1 :(得分:2)
您的第一个问题与mocking and stubbing有关,您在其中创建了从每个类的界面创建的虚假订单和地址。这就是你如何测试Dispatch方法。
然后,您可以通过检查伪造(存根/模拟)对象发生的事情来断言调度方法做了正确的事情。
回答你问题的第二部分; 拥有工厂方法甚至是类层次结构是一个非常好的主意,以便更轻松地编写测试。以良好的方式构建测试同样重要,因为它是构建生产代码。
答案 2 :(得分:1)
我认为您的部分问题可能出在订单对象的设计中。试图写一个独立测试只是为了发现它依赖于其他功能通常表明它们没有充分地解耦。这里可能适用的几条经验法则是:
如果Order.DeliveryAddress只是一个简单的getter / setter,那么不用担心测试它。这就像试图证明C#表现得一样。这样做没什么好处。相反,让您的调度程序测试依赖于此属性处于正常工作状态并不是真正的依赖。
但是,如果Order.DeliveryAddress正在执行逻辑,例如确保地址仅对非调度订单可修改,那么它就更复杂了。您可能不希望尝试分发整个订单,只是为了测试Order.DeliveryAddress之后不再可修改。
在这里调用单一责任原则(参见1和2)会说Order类现在做得太多了。它既调度订单又强制执行订单数据的对象状态完整性。在这种情况下,您可能希望将调度功能拆分为DispatcherService,它只接受订单并调度它,在流程中的订单上设置IsDispatched标志。
然后,您可以通过恰当地设置IsDispatched属性来测试DeliveryAddress行为。
第三种方法(有点作弊但在你试图对遗留对象进行一些测试的情况下运行良好)是将Order子类化为创建一个TestableOrder类,该类向测试夹具公开了修补它的能力。班级的内部状态。换句话说,它可以公开一个MarkAsDispatched()方法,该方法将设置类内部IsDispatched标志,从而允许您测试DeliveryAddress只能在被标记为已分派之前设置。
希望有所帮助。