如何对不可变的类构造函数进行单元测试?
我有一个带有某些私有字段的不可变类,这些私有字段是在构造函数执行期间设置的。我想对该构造函数进行单元测试,但是在这种情况下我不确定"最佳实践"。
简单的例子
此类在Assembly1中定义:
public class Class2Test { private readonly string _StringProperty; public Class2Test() { _StringProperty = ConfigurationManager.AppSettings["stringProperty"]; } }
此类在Assembly2中定义:
[TestClass] public class TestClass { [TestMethod] public void Class2Test_Default_Constructor() { Class2Test x = new Class2Test(); //what do I assert to validate that the field was set properly? } }
编辑1:我已经用潜在的解决方案回答了这个问题,但是我不确定这是否是"正确的方法"。因此,如果我们认为自己有更好的主意,请发布。
这个示例确实不值得测试,但是假设构造函数具有一些更复杂的逻辑。是否最好的方法是避免测试构造函数,并假设该类上所有方法的测试都能正常工作,那么就假设它可以正常工作吗?
编辑2:看起来我使示例有点简单。我已经更新了一个更合理的情况。
解决方案
我已经在Assembly1(代码)上正确启用了[InternalsVisibleTo],以便与Assembly2(测试)建立信任关系。
public class Class2Test { private readonly string _StringProperty; internal string StringProperty { get { return _StringProperty; } } public Class2Test(string stringProperty) { _StringProperty = stringProperty; } }
这使我可以断言:
Assert.AreEqual(x.StringProperty, "something");
对此我唯一不喜欢的是,当我们仅查看Class2Test
时,不清楚内部属性的用途是什么(不加评论)。
其他想法将不胜感激。
没什么,除非我们正在使用该字段。我们不希望通过测试过度规范。换句话说,无需测试赋值运算符是否有效。
如果要在方法或者其他方法中使用该字段,请调用该方法并对其进行断言。
编辑:
assume the constructor has some more complex logic
我们不应在构造函数中执行任何逻辑。
编辑2:
public Class2Test() { _StringProperty = ConfigurationManager.AppSettings["stringProperty"]; }
不要那样做! =)简单单元测试现在已经成为集成测试,因为它取决于一个以上类的成功操作。编写一个处理配置值的类。 WebConfigSettingsReader可以是名称,它应该封装ConfigurationManager.AppSettings
调用。将该SettingsReader类的实例传递到Class2Test的构造函数中。然后,在单元测试中,我们可以模拟WebConfigSettingsReader,并存根对我们可能对其进行的任何调用的响应。
在编辑中,我们现在对ConfigurationManager的依赖度很难测试。
一种建议是提取到该接口的接口,然后使Class2Test ctor以IConfigManager实例作为参数。现在我们可以使用伪造/模拟对象来设置其状态,以便可以测试依赖于Configuration的任何方法以查看它们是否使用了正确的值...
public interface IConfigManager { string FooSetting { get; set; } } public class Class2Test { private IConfigManager _config; public Class2Test(IConfigManager configManager) { _config = configManager; } public void methodToTest() { //do something important with ConfigManager.FooSetting var important = _config.FooSetting; return important; } } [TestClass] public class When_doing_something_important { [TestMethod] public void Should_use_configuration_values() { IConfigManager fake = new FakeConfigurationManager(); //setup state fake.FooSetting = "foo"; var sut = new Class2Test(fake); Assert.AreEqual("foo", sut.methodToTest()); } }