单元测试私人二传手问题(C#)

时间:2009-05-27 22:38:58

标签: c# unit-testing tdd

我正在尝试测试名为AddItem的Order实体方法,我正在尝试确保无法添加重复项。以下是一些示例代码:

[Test]
public void ItemCannotBeAddedTwiceToOrder()
{
    Order o = new Order();
    Item i = new Item("Bike");

    o.AddItem(i);
    o.AddItem(i);

    Assert.AreEqual(o.ItemCount, 1, "A duplicate item was added.");
}

public void AddItem(Item newItem)
{
    if(!CheckForDuplicateItem(newItem))
       _items.Add(newItem);
}

public bool CheckForDuplicateItem(Item newItem)
{
    foreach(Item i in _items)
    {
        if(i.Id == newItem.Id)
          return true;
    }

    return false;
}

所以这是我的问题:如何在测试方法中设置新Item的私有setter Id,以便CheckForDuplicateItem方法可以工作?我想,我不想让那个成员公开进行良好的编码实践。我只是愚蠢,需要让实体项目有一个公共ID设置器?或者我需要使用反射?感谢

注意 - 我正在使用NHibernate进行持久化

5 个答案:

答案 0 :(得分:10)

我通常会为此目的使用反射。这样的事情会起作用:

typeof(Item).GetProperty("Id").SetValue(i, 1, null);

其中1是您要为newItem实例设置的ID。

根据我的经验,您很少需要设置ID,因此最好将setter设为私有。在少数情况下,您需要设置Id以进行测试,只需使用Reflection。

答案 1 :(得分:7)

由于您正在检查订单的行为,因此您可以使用模拟对象作为其项目。 使用模拟对象,您可以定义关于模拟对象将要发生的事情的断言并对其进行测试。 在这种情况下,您可以为每个项定义两个模拟对象,并期望它们的id getter将被调用并返回一个唯一值。然后您可以测试Order行为并检查item的id getter是否按预期调用。 我建议使用Ayende的Rhino Mocks

答案 2 :(得分:4)

虽然Praveen的答案是正确的,毫无疑问是单一用法的,但是在一次又一次强大的域模型测试中,它错过了某种类型的安全性。因此,我将其包装在一个扩展方法中,允许您通过此类型安全调用来设置值:

var classWithPrivateSetters= new ClassWithPrivateSetters();
classWithPrivateSetters.SetPrivate(cwps => cwps.Number, 42);

将它放入你的测试组件中,你很高兴

public static class PrivateSetterCaller
{
    public static void SetPrivate<T,TValue>(this T instance, Expression<Func<T,TValue>> propertyExpression, TValue value)
    {
        instance.GetType().GetProperty(GetName(propertyExpression)).SetValue(instance, value, null);
    }

    private static string GetName<T, TValue>(Expression<Func<T, TValue>> exp)
    {
        MemberExpression body = exp.Body as MemberExpression;

        if (body == null)
        {
            UnaryExpression ubody = (UnaryExpression)exp.Body;
            body = ubody.Operand as MemberExpression;
        }

        return body.Member.Name;
    }
}

答案 3 :(得分:0)

另一种解决方案是通过派生类并在派生类中公开成员来使私有成员可访问。这对于测试来说是非常多的开销,而Visual Studio只有build-in support for private methods

答案 4 :(得分:0)

我想你可能会忽略这一点。您是否因为不希望多次调用数据库而避免多次添加?我认为NHibernate免费提供给你。或者,您是否应该使用Set?调用者可能会添加或不添加项目的含义是什么?

如果没有持久性问题,您可以添加两个具有相同ID的不同项目,并确认您只有第一个。必须有某种方法来检测哪些项目在订单中,或者添加它们没有意义......