代码之家  ›  专栏  ›  技术社区  ›  William

如何测试通过公共方法修改的私有字段

  •  5
  • William  · 技术社区  · 14 年前

    任何人都能用一种建议的方法来指导我测试通过公共方法修改的类中的私有字段吗?我读过很多人的评论,他们建议不要建议测试私有成员,因为它们是实现内部的,但是这个场景 似乎 与其他答案不同。

    我在考虑让私有字段受保护,并创建一个公开该字段的测试子类,但是如果我不能修改类的内部呢?

    在下面的示例代码中,要测试的私有成员将是_values,它是一个只写的集合,通过addvalue()接收新值。

    public class Sample
    {
    
        private Dictionary<string, string> _values;
        private DateTime _created;
    
        public Sample()
        {
            _values = new Dictionary<string, string>();
            _created = DateTime.Now;
        }
    
        public void AddValue(string key, string value)
        {
            _values.Add(key, value);
        }
    }
    
    5 回复  |  直到 14 年前
        1
  •  2
  •   stakx - no longer contributing Saravana Kumar    14 年前

    我想这是一个例子 依赖注入 可能会有所帮助。

    这里所发生的是您想要测试一个内部对象(字典 _values )已通过呼叫正确更新 AddValue . 你可以通过在你的测试类中插入一个模拟字典来实现这一点。

    可以这样做,例如如下。首先,你必须改变你的 Sample 稍微上一点课:

    public class Sample
    {
        private IDictionary<string, string> _values = new Dictionary<string, string>();
    
        protected virtual IDictionary<string, string> GetDictionary()
        {
            return this._values;
        }
    
        public void AddValue(string key, string value)
        {
            GetDictionary().Add(key, value);
            //    ^^^
            // notice this!
        }
    }
    

    现在,您可以通过从您的 样品 通过重写 InitializeDictionary 方法:

    // this derived class is only needed in your test project:
    internal class SampleTest : Sample
    {
        public SampleTest(IDictionary<string, string> dictionaryToUse)
        {
            this._dictionaryToUse = dictionaryToUse;
        }
    
        private IDictionary<string, string> _dictionaryToUse;
    
        protected override IDictionary<string, string> GetDictionary()
        {
            return this._dictionaryToUse;
        }
    }
    

    在测试设置中,现在可以测试 SampleTest 类而不是您的 样品 班级。这应该是正常的,因为派生类是相同的 除了 它允许您指定将在内部使用的字典。用于检查的单元测试 附加值 现在看起来像这样:

    [Test]
    public void AddValue_addSomething_DictionaryHasOneAdditionalEntry()
    {
        var mockDictionary = new Dictionary<string, string>();
        var sample = new SampleTest(mockDictionary);
        var oldCount = mockDictionary.Count;
    
        sample.AddValue(...);
    
        Assert.AreEqual(oldCount + 1, mockDictionary.Count);
    }
    

    免责声明: 我决不是单元测试专家,所以我的示例可能有缺陷,甚至过于复杂。我只是想证明你 可以 如果您以合理的可测试方式(例如,通过允许依赖项注入的方式)设计类,则测试类的内部属性。

        2
  •  4
  •   µBio    14 年前

    您仍然不需要测试您的私有变量。你测试你的接口。在这种情况下,我可能会添加 Count 属性并将其用于某些测试(或这些行中的某些内容)

        3
  •  2
  •   Rob    14 年前

    如果你想验证一下 _values 包含您拨打电话后所期望的内容 Sample.AddValue 将会改变:

    private Dictionary<string, string> _values;
    

    成为

    internal Dictionary<string, string> _values;
    

    然后添加一个 InternalsVisibleTo attribute 到您的项目的assemblyinfo.cs,引用您的测试项目。然后,这将向测试项目公开\u values字段。

    这远非理想,因为您将要“测试”一个内部实现细节,但如果是这样或什么都没有,那就是一个起点!

        4
  •  2
  •   Timwi    14 年前

    我认为单元测试的思想是通过对象的公共表面而不是内部实现来测试它的行为。换句话说,您不应该访问 _values 在你的测试中,你应该打电话给公众 AddValue() 方法添加一些键/值,然后添加其他公共方法,例如 GetKeys() 或者别的什么,把信息再拿出来。然后,您的单元测试应该根据输入的信息测试该信息是否正确。

    这主要是因为单元测试不应该对实现细节做任何假设。想象你需要改变 Dictionary<> 变成一个 SortedDictionary<> 以后,无论出于什么原因,或者突然需要两个单独的字典。您不需要对单元测试进行任何更改,这样就可以工作;您应该能够更改内部实现,然后使用您已经获得的相同单元测试验证其正确性。

        5
  •  1
  •   linkerro    14 年前

    正如Rob所指出的,不建议您访问私有字段,但如果您不得不并且无法实际测试该字段所指定的值,则可以这样做:

            Type sampleType = sampleInstance.GetType();
            FieldInfo fieldInfo = sampleType.GetField("_values", BindingFlags.Instance, BindingFlags.NonPublic);
            Dictionary<string, string> info = fieldInfo.GetValue(sampleType);