如何对不可变类构造函数进行单元测试?

时间:2008-09-25 00:09:13

标签: c# unit-testing

我有一个不可变类,其中包含一些在构造函数执行期间设置的私有字段。我想对这个构造函数进行单元测试,但在这种情况下我不确定“最佳实践”。

简单示例

此类在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 :看起来我做的样本有点简单。我已经用更合理的情况更新了它。

3 个答案:

答案 0 :(得分:8)

没有,除非你使用那个领域。您不希望通过测试进行过度规范。换句话说,没有必要测试赋值运算符的工作原理。

如果您在某个方法或某个方法中使用该字段,请调用该方法并对其进行断言。

编辑:

  

假设构造函数有一些更复杂的逻辑

你不应该在构造函数中执行任何逻辑。

编辑2:

public Class2Test()
{
     _StringProperty = ConfigurationManager.AppSettings["stringProperty"];
}

不要那样做! =)您的简单单元测试现在已成为集成测试,因为它取决于多个类的成功操作。编写一个处理配置值的类。 WebConfigSettingsReader可以是名称,它应该封装ConfigurationManager.AppSettings调用。将该SettingsReader类的实例传递给Class2Test的构造函数。然后,在您的单元测试中,您可以模拟您的WebConfigSettingsReader,并对您可能对其进行的任何调用存根响应。

答案 1 :(得分:1)

我已在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内部财产的目的时,不清楚(没有评论)。

非常感谢其他想法。

答案 2 :(得分:1)

在编辑中,您现在依赖于难以测试的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());
        }
    }