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

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

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

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

public class Sample { private Dictionary _values; private DateTime _created; public Sample() { _values = new Dictionary(); _created = DateTime.Now; } public void AddValue(string key, string value) { _values.Add(key, value); } } 

我认为这将是dependency injection可能有所帮助的一个例子。

这里发生的是你想测试一个内部对象(字典_values )是否通过调用AddValue正确更新。 你可以通过在你的测试中注入一个模拟字典来实现这一点。

这可以例如如下进行。 首先,您必须稍微更改Sample类:

 public class Sample { private IDictionary _values = new Dictionary(); protected virtual IDictionary 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 dictionaryToUse) { this._dictionaryToUse = dictionaryToUse; } private IDictionary _dictionaryToUse; protected override IDictionary GetDictionary() { return this._dictionaryToUse; } } 

在测试设置中,您现在可以测试此SampleTest类而不是Sample类。 这应该没问题,因为派生类是相同的, 除了它允许您指定它将在内部使用的字典。 检查AddValueunit testing现在看起来像这样:

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

免责声明:我绝不是一个unit testing专家,所以我的例子可能有缺陷,甚至太复杂了。 我的目的仅仅是为了certificate如果你以合理可测试的方式设计你的类,你可以测试一个类的内部属性 – 例如通过允许dependency injection的方法。

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

如果您想要在调用Sample.AddValue之后validation_values包含您期望的内容,那么您将拥有一个选项:

 private Dictionary _values; 

成为

 internal Dictionary _values; 

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

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

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

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

正如Rob所指出的那样,你不建议你访问私有字段,但是如果你绝对必须并且实际上无法测试被赋予该字段的值,你可以这样做:

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