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

时间:2010-07-30 23:10:35

标签: c# .net unit-testing

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

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

在下面的示例代码中,要测试的私有成员将是_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 个答案:

答案 0 :(得分:4)

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

答案 1 :(得分:2)

如果您想要在_values拨打电话后确认Sample.AddValue包含您的预期,那么您将拥有一个选项:

private Dictionary<string, string> _values;

internal Dictionary<string, string> _values;

然后为项目的AssemblyInfo.cs添加InternalsVisibleTo attribute,引用您的测试项目。然后,这会将_values字段暴露给您的测试项目。

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

答案 2 :(得分:2)

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

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

答案 3 :(得分: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);
}

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

答案 4 :(得分:1)

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

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