.net 我应该如何模拟这个简单的服务层方法?

.net 我应该如何模拟这个简单的服务层方法?,.net,unit-testing,testing,mocking,.net,Unit Testing,Testing,Mocking,在我的服务层中,我有以下简单的方法。我不确定如何从存储库开始模拟不同的链接部分 public ICollection<GameFile> FindAllActiveGamesFiles() { return _gameFileRepository // <- this is an IRepository<GameFile> .Find() // <-- returns an IQueryable<T> .. in this

在我的服务层中,我有以下简单的方法。我不确定如何从存储库开始模拟不同的链接部分

public ICollection<GameFile> FindAllActiveGamesFiles()
{
    return _gameFileRepository // <- this is an IRepository<GameFile>
        .Find() // <-- returns an IQueryable<T> .. in this case, an IQueryable<GameFile>
        .WhereIsActive() // <-- a simple Pipe/Filter; 'where x.IsActive'
        .ToList();
}

听起来您想编写一个测试来演示Linq语句的覆盖范围。您已经指出,您的存储库是一个接口(IRepository),应该进行模拟。您只需要一些示例,说明如何证明您的服务层正确过滤存储库的内容

据我所知,这是您的ServiceLayer

public class ServiceLayer
{
     private readonly IRepository<GameFile> _gameRepository;

     public SerivceLayer(IRepository<GameFile> repository)
     {
         _gameRepository = repository;
     }

     public IEnumerable<GameFile> FindAllActiveGamesFiles()
     {
         return _gameRepository
                    .Find()  // method we need to mock
                    .Where( gameFile => gameFile.IsActive)
                    .ToList();
     }
}
或者,也许您想包装异常

[Test]
[ExpectedException(typeof(GameFileNotFoundException))]
public void WhenTheRepositoryFails_ServiceLayer_ShouldReportCustomError()
{
    Mock.Get(Repository)
        .Setup( r => r.Find())
        .Throws( new InvalidOperationException() );

    Subject.FindAllActiveGameFiles();
}

通过断言从
ToList()
返回的值,似乎您正在尝试测试实际的方法
Find()
WhereIsActive()
ToList()
。如果是这样,则不需要使用模拟对象

相反,我会为每种方法编写单独的测试。这是sane单元测试的关键。
FindAllActiveGamesFiles()
的测试应该避免测试它们(如果可以的话)。您应该尽可能只测试
FindAllActiveGamesFiles()
中的代码

接下来,
FindAllActiveGamesFiles()
的测试需要设置
mockRepository
的期望值。假设
Find()
WhereIsActive()
都返回相同的存储库,您将设置mock作为这些方法的返回值

我不知道您的模拟库的API,但这是它在psuedocode中的外观:

Mock<IRepository<GameFile>> mockRepository = new Mock<IRepository<GameFile>>();
List<GameFile> result = new List<GameFile>();
mockRepository.expect('Find')->willReturn(mockRepository);
mockRepository.expect('WhereIsActive')->willReturn(mockRepository);
mockRepository.expect('ToList')->willReturn(result);
IGameFileService service = new GameFileService(mockRepository, fakeLoggingService);

assertSame(service.FindAllActiveGamesFiles(), result);
Mock mockRepository=new Mock();
列表结果=新列表();
mockRepository.expect('Find')->willReturn(mockRepository);
mockRepository.expect('WhereIsActive')->willReturn(mockRepository);
mockRepository.expect('ToList')->willReturn(结果);
IGameFileService=newgamefileservice(mockRepository,fakeLoggingService);
assertSame(service.FindAllActiveGamesFiles(),result);

如您所见,测试代码比正在测试的代码更复杂。这是一个好迹象,表明您可能不需要测试。

我很困惑!您不模拟方法,而是模拟类型/接口。那么你的意思是如何为单元测试设置期望值呢?听起来你想通过模拟各种调用来对这个方法进行单元测试。我认为上面的例子太简单,无法测试。如果您的测试代码最终比测试中的代码更复杂,那么您是在浪费时间。如果你有类似的、更复杂的链式方法要模拟,你基本上需要为每个返回的对象创建一个模拟,并且有一个单一的期望(如果链中的每个方法都返回一个不同的对象)。它可以是一个真正的PITA来设置和验证,因此只有当传递给单个方法调用的参数很复杂并且需要测试时,它才有回报。不要浪费时间测试
userName
参数是否已传递给
byUserName()
@David Harkness&@Aliostad-我已经更新了OP以进一步解释我的问题。我知道上面的场景可能令人懊悔,但这仍然是一个真实的例子,我觉得我仍然需要测试这个方法。您在FindalActivegamesFiles()中到底测试了什么?它只是调用_gameFileRepository.Find().WhereIsActive().ToList();您已经在测试这些方法了吗?如果是这样,那么你就完了谢谢你。这是一个很好的答案,有各种深入的例子。我希望我能给你更多的分数。干得好,谢谢你,伙计!是否无法单独测试
WhereIsActive()
?随着您添加更多的查询方法(
FindAllActiveGameFilesOlderThan(date)
FindAllActiveGameFilesOwnedBy(user)
,等等),具有重复代码的测试方法的数量将显著增加,您将多次测试相同的代码路径。我只是简化了示例,以说明需要模拟的是Find方法。一旦有了IQueryable,扩展方法就可以简单地工作了。功能性善良FTW!
[Test]
public void WhenTheRepositoryFails_ServiceLayer_ShouldHandleExceptionsGracefully()
{
    Mock.Get(Repository)
        .Setup( r => r.Find())
        .Throws( new InvalidOperationException() );

    Results = Subject.FindAllActiveGameFiles();

    Assert.AreEqual(0, Results.Count);          
}
[Test]
[ExpectedException(typeof(GameFileNotFoundException))]
public void WhenTheRepositoryFails_ServiceLayer_ShouldReportCustomError()
{
    Mock.Get(Repository)
        .Setup( r => r.Find())
        .Throws( new InvalidOperationException() );

    Subject.FindAllActiveGameFiles();
}
Mock<IRepository<GameFile>> mockRepository = new Mock<IRepository<GameFile>>();
List<GameFile> result = new List<GameFile>();
mockRepository.expect('Find')->willReturn(mockRepository);
mockRepository.expect('WhereIsActive')->willReturn(mockRepository);
mockRepository.expect('ToList')->willReturn(result);
IGameFileService service = new GameFileService(mockRepository, fakeLoggingService);

assertSame(service.FindAllActiveGamesFiles(), result);