【问题标题】:How to test private fields that are modified via public methods如何测试通过公共方法修改的私有字段
【发布时间】:2010-07-30 23:10:34
【问题描述】:

任何人都可以指导我测试通过公共方法修改的类中的私有字段的建议方法。我读过很多人建议不建议测试私有成员,因为它们是实现的内部成员,但是这种情况似乎与大多数其他答案不同。

我正在考虑保护私有字段并创建一个公开该字段的测试子类,但是如果我无法修改该类的内部结构怎么办?

在下面的示例代码中,要测试的私有成员是 _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);
    }
}

【问题讨论】:

  • 这个例子有什么“不同”?该类提供了哪些您需要测试的功能?
  • @Rex 我不确定它是否不同,我认为它“似乎”不同。在这种情况下,内部元素只是一个存储区域。我想测试公共方法是否正常工作,但是唯一的方法是检查私有字段。 (真正的类在 AddValue 方法中会有更复杂的逻辑)

标签: c# .net unit-testing


【解决方案1】:

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

【讨论】:

  • 也许我想了很多。添加一个简单的 Count 属性确实会很好。
【解决方案2】:

我认为这将是一个依赖注入可能会有所帮助的示例。

这里发生的事情是您想测试内部对象(字典_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!
    }
}

现在,您可以通过从 Sample 类派生并通过覆盖 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 类而不是您的Sample 类。这应该没问题,因为派生类是相同的除了,它允许您指定它将在内部使用的字典。检查AddValue 的单元测试现在看起来像这样:

[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);
}

免责声明:我绝不是单元测试专家,所以我的示例可能存在缺陷甚至过于复杂。我的目的只是为了证明如果您以合理可测试的方式设计您的类 - 例如,您可以测试一个类的内部属性。通过允许依赖注入的方式。

【讨论】:

  • 我就是这样做的。
【解决方案3】:

如果您想验证_values 是否包含您在调用Sample.AddValue 后所期望的内容,您可以选择更改:

private Dictionary<string, string> _values;

成为

internal Dictionary<string, string> _values;

然后将InternalsVisibleTo attribute 添加到您的项目的 AssemblyInfo.cs 中,引用您的测试项目。然后,这会将 _values 字段公开给您的测试项目。

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

【讨论】:

  • 有趣的想法 Rob,讨厌,但就像你说的那样,这是一个起点
【解决方案4】:

我认为单元测试的想法是通过对象的公共表面而不是内部实现来测试对象的行为。换句话说,你不应该在你的测试中访问_values;您应该改为调用公共 AddValue() 方法来添加一些键/值,然后调用其他一些公共方法,例如GetKeys() 或其他东西,再次获取信息。然后,您的单元测试应该根据输入的信息测试该信息是否正确。

这样做的主要原因是单元测试不应该对实现细节做出任何假设。想象一下,无论出于何种原因,您稍后需要将Dictionary&lt;&gt; 更改为SortedDictionary&lt;&gt;,或者您突然需要两个单独的字典。您不需要对单元测试进行任何更改即可使其正常工作;您应该能够更改内部实现,然后使用您已经获得的相同单元测试来验证其正确性。

【讨论】:

  • 不知道为什么你被拒绝了 Timiwi,但是内部字典不能公开,它必须是私有的并且只能写。
  • @WDuffy:我的回答中没有任何内容表明它会公开。目前尚不清楚您所说的“只写”是什么意思。如果你真的只写过字典,再也没有读过它,那么它没有任何意义,你可以完全删除它。
【解决方案5】:

正如 Rob 指出的那样,不建议您访问私有字段,但如果您必须并且无法实际测试分配给该字段的值,您可以这样做:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-19
    • 2019-12-11
    • 1970-01-01
    • 2013-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-17
    相关资源
    最近更新 更多