【问题标题】:Unit Testing Private Setter Question (C#)单元测试私有 Setter 问题 (C#)
【发布时间】:2009-05-27 22:38:58
【问题描述】:

我正在尝试测试一个名为 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;
}

所以这是我的问题:如何在测试方法中设置新项目的私有设置器 ID,以便 CheckForDuplicateItem 方法可以工作?我想我不想因为良好的编码实践而公开该成员。我只是愚蠢并且需要使实体 Item 具有公共 Id 设置器吗?还是我需要使用反射?谢谢

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

【问题讨论】:

    标签: c# unit-testing tdd


    【解决方案1】:

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

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

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

    根据我的经验,您很少需要设置 Id,因此最好将 setter 保留为私有。在少数情况下,您确实需要为测试目的设置 Id,只需使用反射即可。

    【讨论】:

    • 非常感谢。我试试看。
    【解决方案2】:

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

    【讨论】:

    • 好点。我会研究嘲笑。我听说过很多关于 Rhino Mocks 的好消息。
    【解决方案3】:

    虽然 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;
        }
    }
    

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

        我认为您可能在这里忽略了重点。您是否因为不想多次调用数据库而避免多次添加?我认为 NHibernate 免费为您提供。或者,您应该使用 Set 吗?可能会或可能不会添加项目对调用者有什么影响?

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

        【讨论】:

          猜你喜欢
          • 2012-04-07
          • 1970-01-01
          • 2011-06-26
          • 2013-09-01
          • 2016-01-27
          • 1970-01-01
          • 1970-01-01
          • 2018-01-15
          • 2016-10-25
          相关资源
          最近更新 更多