1

一天内我的第二个 MSpec 问题,这是一个新记录。我正试图很快地在 MSpec 上变得聪明,但我遇到了一些 MSpec 一直存在的老问题。

场景:我有一个包含一堆卡通的存储库。现在我只需要在一个名称参数上过滤这个集合,它是一个字符串。有人告诉我,稍后我需要对更多属性进行过滤,因此我决定创建一个类,该类通过 IoC 接收我的 ICartoonRepository,并包含一个名为 GetByName(string name) 的简单方法。

您可能会争辩说这是矫枉过正,但我​​正在尝试自学如何使用 MSpec 并以更 TDD 的方式工作。

所以我创建了以下内容:

[Subject(typeof(CartoonViewModelBuilder))]
public class when_cartoon_repository_is_asked_to_get_by_id : specification_for_cartoon_viewmodel_builder
{
    static string name;
    static Cartoon the_cartoon;
    static Cartoon result;

    Establish context = () =>
    {
        name = "Taz";
        the_cartoon = new Cartoon();
        the_cartoon_repository.Stub(r => r.GetAll().Where(x=>x.Name == name).FirstOrDefault()).Return(the_cartoon);
    };

    Because of = () => result = subject.GetByName(name);

    It should_return_cartoon = () => result.ShouldBeTheSameAs(the_cartoon);
}

由于存储库为空,因此在存根上失败。我还有其他几个测试通过(简单地测试 GetAll() 等)。我需要向存储库添加东西来测试它吗?这就是我难过的地方,请温柔一点。

另外,如果我在存根中编写 linq 语句,我似乎在实际实现和测试中做了两次。这是重点吗?感觉不对。有没有更好的方法可以编写这个测试?

为了清楚起见,这里是实际的实现(我省略了接口和类,它们只有一个属性:

public class CartoonViewModelBuilder: ICartoonViewModelBuilder
{
    readonly ICartoonRepository _cartoonRepository;

    public CartoonQueryObject(ICartoonRepository  cartoonRepository)
    {
        _cartoonRepository = cartoonRepository;
    }

    public IList<Cartoon> GetAllCartoons()
    {
        return _cartoonRepository.GetAll();
    }

    public Cartoon GetByName(string name)
    {
        return _cartoonRepository.GetAll().Where(x => x.Name == name).FirstOrDefault();
    }
}

编辑 1:基于缺乏响应,我应该说,如果我使用 NUnit 之类的东西,我会在测试类上创建一个类似“LoadDummyData”的方法并将数据扔到存储库中,然后我会进行复杂的过滤或查看模型构建并手动检查发生了什么。这使得大型重构成为一件苦差事。看起来规格允许您避免这种情况?

编辑2:这是我现在通过的更正测试。如果我做得对,请告诉我,我想我是。再次感谢牵手!

    static string name;
    static Cartoon the_cartoon;
    static Cartoon result;
    static IQueryable<Cartoon> the_cartoons;

    Establish context = () =>
    {
        name = "Taz";
        the_cartoon = new Cartoon {Name = name};
        the_cartoons = new List<Cartoon> {the_cartoon, new Cartoon(), new Cartoon() }.AsQueryable();
        the_cartoon_repository.Stub(r => r.GetAll()).Return(the_cartoons.ToList());
    };

    Because of = () => result = subject.GetByName(name);

    It should_return_cartoon = () => result.ShouldBeTheSameAs(the_cartoon);

编辑3:给你两分,但不幸的是我只能奖励一个最佳答案。

4

2 回答 2

1

此测试失败的实际原因是您模拟存储库的方式。r.GetAll().Where(x=>x.Name == name).FirstOrDefault()如果可以如此轻松地模拟类似方法链,我会感到非常惊讶,因为它使用 LINQ 扩展方法和 lambda 子句。该框架应该真正抛出 NotSupported 异常或让您知道您不能作为一个整体模拟 LINQ 查询的东西。

要模拟 LINQ 查询结果,您应该提供适当准备的底层数据集合,这是 LINQ 查询的起点。在您的示例中,您应该模拟只是r.GetAll()返回一个包含具有正确名称的元素的集合。实际查询将在您的“模拟”数据上运行并检索您期望的对象。

正如您所指出的,这消除了在代码和测试中复制 LINQ 查询的需要,这很奇怪。

编辑:您编辑中的代码就像我建议的那样,技术上还可以。

无论如何,正如您所说,到目前为止,这有点矫枉过正。除了调用模拟存储库之外,您的测试类不做任何事情,因此该测试的价值相当小。但是,如果您要在GetByName方法中有更多逻辑,这可能是一个好的开始。

于 2011-06-19T08:23:18.643 回答
1

如果您想测试您的存储库实现,请不要存根!无论是否是 MSpec,我都会将已知项目列表添加到存储库中,然后使用 GetByName 发出查询。然后断言只返回了您期望的项目。我也会使用 ShouldEqual,因为存储库可能会处理您添加的项目并返回不同的实例,尽管被认为是相等的(聚合 ID 是相等的)。

于 2011-06-19T08:30:22.147 回答