使用MSpec对存储库进行单元测试,我这样做了吗?

时间:2011-06-19 01:51:33

标签: unit-testing mspec

我在一天内发出第二个MSpec问题,这是一个新记录。我正试图在MSpec上很快变得聪明,而且我遇到了一些我在MSpec上遇到过的老问题。

场景:我有一个包含大量漫画的存储库。现在我只需要在一个 Name 参数上过滤这个集合,这是一个字符串。据我所知,我稍后需要在更多属性上对此进行过滤,我决定创建一个通过IoC接收ICartoonRepository的类,并包含一个名为GetByName(字符串名称)的简单方法。

你可能会认为这有点矫枉过正,但我​​正在努力教会自己如何使用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:给你们两个积分,但遗憾的是我只能给出一个最佳答案。

2 个答案:

答案 0 :(得分:1)

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

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

这样就无需在代码和测试中复制LINQ查询,这很奇怪,正如您所指出的那样。

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

无论如何,就像你说的那样,现在它有点矫枉过正了。除了对模拟存储库的调用之外,您的测试类不会执行任何操作,因此该测试的值相当小。但如果你要在GetByName方法中使用更多逻辑,那么这可能是一个好的开始。

答案 1 :(得分:1)

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