5

问题

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

背景

我正处于使用 ASP.Net MVC 的初学者阶段,并且正在使用 Steven Sanderson 的 Sports Store 和 Scott Gu 的 Nerd Dinner 作为示例。我刚刚遇到的一个小问题是如何在进行伪造时使用只读属性。我正在使用 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 是只读的,所以上面的代码实际上不起作用。但是,我不知道如何创建一个假的新人并设置一个只读属性。我确信有一个解决方案,但我在搜索中还没有遇到它。

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

我还没有决定解决这个问题的坚定方法。我不喜欢为了制造假货而修改我的域类的想法。对我来说,为域类添加标记以进行测试是一种增加耦合的形式——耦合到测试的实现。我现在正在研究另一种可能性,即创建一个 FakePerson 类,该类继承自 Person,但使用新的读写属性隐藏属性。

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

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

获胜者:模拟框架

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

4

5 回答 5

6

考虑在测试中模拟 Person 类型。使用起订量的示例:

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 如何设置这些只读属性。

编辑:如果您尝试上述操作并且 MoqArgumentExceptionSetupGet调用中抛出消息“不可覆盖成员上的无效设置:p => p.id”,那么您需要将该属性标记为virtual。这将需要为您希望覆盖其吸气剂的每个属性完成。

在 LINQ to SQL 中,这可以通过选择属性在 OR 设计器中完成,然后在“属性”窗口中将Inheritance Modifier设置为virtual

于 2009-06-09T07:06:23.423 回答
1

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

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

于 2009-06-08T16:13:26.160 回答
1

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

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

于 2009-06-08T15:41:36.693 回答
0

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

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

于 2009-06-08T20:12:23.947 回答
0

我也使用最小起订量。我喜欢它,而且效果很好。但是,在我开始使用 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);
    }

}
于 2009-08-19T16:16:58.473 回答