我在一天内发出第二个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:给你们两个积分,但遗憾的是我只能给出一个最佳答案。
答案 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相等)。