在TDD和DDD中,如何处理假货中的只读属性?

时间:2009-06-08 15:38:00

标签: asp.net-mvc tdd domain-driven-design nerddinner

问题

创建假货时如何处理只读字段?

背景

我正处于使用ASP.Net MVC的初学阶段,我正在使用Steven Sanderson的体育用品店和Scott Gu的书呆子晚餐作为例子。我刚刚遇到的一个小问题是如何在做假货时使用只读属性。我正在使用LINQToSQL。

我的界面是:

public interface IPersonRespository
{   
    Person GetPerson(int id);
}

我的假冒成了

public class FakePersonRepository
{
    public Person GetPerson(int id)
    {
        return new Person {id="EMP12345", name="John Doe", age=47, ssn=123-45-6789, totalDrWhoEpisodesWatched=42};
    }
}

这是我的问题。字段id,ssn和totalDrWhoEpisodesWatched是只读的,因此上述代码实际上不起作用。但是,我不知道如何创建假新人并设置只读属性。我确信有一个解决方案,但我在搜索中还没有遇到过它。

更新:继承+属性隐藏为潜在解决方案?

我还没有决定解决这个问题。我不喜欢为了创造假货而修改我的Domain类的概念。对我来说,为了进行测试而在域类中添加标记是一种额外的耦合形式 - 与测试的实现相结合。我现在正在研究另一种可能性,即创建一个继承自Person的FakePerson类,但是使用新的读写属性隐藏属性。

public class FakePerson: Person
{
    public new int age { get; set; }
    public new string ssn { get; set; }
    public new int totalDrWhoEpisodesWatched { get; set; }
}

到目前为止,这个解决方案就是我的倾向。它确实打破了Liskov替换原则,但是这并没有在测试项目中给我带来太多麻烦。我很高兴听到任何批评和/或反馈作为解决方案。

获奖者:模拟框架

Moq似乎可以胜任这项工作。事实上,我通过继承隐藏属性的最后一个解决方案确实有效,但是通过使用Moq,我获得了一组更易于维护的标准化功能。我假设其他模拟框架具有此功能,但我没有检查。据说Moq开始模拟写作更直接,我现在肯定是这样。

5 个答案:

答案 0 :(得分:6)

考虑模拟测试中的Person类型。使用Moq的示例:

var mock = new Mock<Person>();
mock.SetupGet(p => p.id).Returns("EMP12345");
mock.SetupGet(p => p.ssn).Returns("123-45-6789");
mock.SetupGet(p => p.totalDrWhoEpisodesWatched).Returns(42);
return mock.Object;

否则,请尝试了解LINQ to SQL如何设置这些只读属性。

编辑:如果您尝试上述操作,Moq会在ArgumentException来电中抛出SetupGet,并且消息“无法覆盖的成员上的设置无效: p =&gt; p.id“,然后您需要将该属性标记为虚拟。对于要覆盖其吸气剂的每个属性,都需要这样做。

在LINQ to SQL中,可以通过选择属性在OR设计器中完成,然后在“属性”窗口中将继承修饰符设置为虚拟

答案 1 :(得分:1)

您只能在类的构造函数中设置只读属性。 Person对象应该有一个接受id,ssn和totalDrWhoEpisodesWatched的构造函数。当然,如果这是一个linqtosql生成的对象,那么在代码自动生成时可能会出现修改问题。

您可以考虑使用映射对象在您的存储库中公开...所以您实际上不必使用您的linqtosql对象作为您的模型。

答案 2 :(得分:1)

在.NET中,您可以将您的setter标记为“internal”,并使用InternalsVisibleTo程序集属性使内部对测试程序集可见。这样你的安装人员就不会公开,但你仍然可以访问它们。

注意:尽管问题没有标记为.NET,但我认为它是基于对象初始化程序语法的使用。如果我的假设是错误的,则此建议不适用(当然,除非您使用的语言具有相同的功能)。

答案 3 :(得分:0)

如果是测试 - 请考虑使用反射。这不会涉及搞乱您的域模型。

例如 - 我得到了FactoryBase类,它使用反射通过参数(如this)通过lambda表达式设置所需的prop。像魅力一样工作 - 创建新工厂很简单,如定义存储库类型和默认实体数据。

答案 4 :(得分:0)

我也使用Moq。我喜欢它并且效果很好。但是,在我开始使用Moq之前,我写了许多假货。这就是我如何使用假货解决问题。

由于假冒可能有“生产”实现没有的其他方法,我会在假实现中添加一些额外的方法来处理设置只读部分。

像这样:

public class FakePersonRepository : IPersonRespository
{
    private IDictionary<int, Person> _people = new Dictionary<int, Person>();

    public Person GetPerson(int id)  // Interface Implementation
    {
        return _people(id);
    }

    public void SetPerson(int id, Person person)  // Not part of interface
    {
         _people.Add(id, person);
    }

}