How should I be Mocking this simple Service layer method?
I have the following simple method, in my Service layer. I'm not sure how I should mock out the different chained parts, starting from the repository?
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();
}
My preference is to use Moq, but I'm very happy to see other implimentations .. mainly because i'm not after the exact syntax answer, but the theortical answer. Ie. U need to mock the IRepository<GameFile>
and setup the ToList()
method only .. blah blah blah...
That's the stuff I don't get what I should be mocking.
Cheers :)
--- Update : to clarify my question
What i'm trying to test is that the method 'FindAllActiveGamesFiles()' works. As such, I believe I need to mock the gameFileRepository (which is an interface). I'm not sure if this is the ONLY thing i should be mocking in this example.
eg.
[TestMethod]
public void MyTest()
{
// Arrange.
Mock<IRepository<GameFile>> mockRepository = new Mock<IRepository<GameFile>>();
mockRepository.Setup(....).MoreStuffToDo(...); // <-- that's what i'm unsure about.
IGameFileService = new GameFileService(mockRepository, fakeLoggingService);
/开发者_StackOverflow社区/ Act.
var gameFiles = gameFileService.FindAllActiveGamesFiles();
// Asserts.
Assert.IsNotNull(gameFiles);
CollectionAssert.AllItemsAreNotNull(gameFiles.ToArray());
// .. and more asserts ///
// What about expectations? eg. that ToList() was entered/called?
}
Sounds like you want to write a test that demonstrates coverage of your Linq statement. You've already pointed out that your repository is a interface (IRepository<GameFile>) and should be mocked. You just need some examples of how to demonstrate that your service layer filters the contents of the repository correctly.
Here's your ServiceLayer as I understand it.
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();
}
}
Let's write some tests.... (NUnit and Moq)
[TestFixture]
public class ServiceLayerFixture
{
protected IRepository<GameFile> Repository;
protected ServiceLayer Subject;
protected ICollection<GameFile> Results;
[Setup]
public void Setup()
{
// create our mock
Repository = new Mock<IRepository<GameFile>>().Object;
// initialize our test subject
Subject = new ServiceLayer(Repository);
}
[Test]
public void WhenRepositoryDoesNotContainItems_ServiceLayer_ReturnsAnEmptyCollection()
{
Mock.Get(Repository)
.Setup( r => r.Find())
.Returns( new List<GameFile>().AsQueryable() );
Results = Subject.FindAllActiveGameFiles();
Assert.AreEqual(0, Results.Count);
}
[Test]
public void WhenRepositoryDoesNotContainActiveItems_ServiceLayer_ReturnsAnEmptyCollection()
{
Mock.Get(Repository)
.Setup( r => r.Find())
.Returns(
new List<GameFile>()
{
new GameFile { IsActive = false },
new GameFile { IsActive = false }
}.AsQueryable() );
Results = Subject.FindAllActiveGameFiles();
Assert.AreEqual(0, Results.Count);
}
[Test]
public void WhenRepositoryContainActiveItems_ServiceLayer_FiltersItemsAppropriately()
{
Mock.Get(Repository)
.Setup( r => r.Find())
.Returns(
new List<GameFile>()
{
new GameFile { IsActive = true },
new GameFile { IsActive = false }
}.AsQueryable() );
Results = Subject.FindAllActiveGameFiles();
Assert.AreEqual(1, Results.Count);
}
}
Where your code stops short is that you could be handling exceptions from your IRepository more gracefully.
So consider:
[Test]
public void WhenTheRepositoryFails_ServiceLayer_ShouldHandleExceptionsGracefully()
{
Mock.Get(Repository)
.Setup( r => r.Find())
.Throws( new InvalidOperationException() );
Results = Subject.FindAllActiveGameFiles();
Assert.AreEqual(0, Results.Count);
}
Or, maybe you want to wrap the exception?
[Test]
[ExpectedException(typeof(GameFileNotFoundException))]
public void WhenTheRepositoryFails_ServiceLayer_ShouldReportCustomError()
{
Mock.Get(Repository)
.Setup( r => r.Find())
.Throws( new InvalidOperationException() );
Subject.FindAllActiveGameFiles();
}
By asserting things about the value returned from ToList()
, it appears that you are trying to test the actual methods Find()
, WhereIsActive()
, and ToList()
. If so you don't need to use mock objects.
Instead, I would write separate tests for each of those methods. This is the key to sane unit testing. The test for FindAllActiveGamesFiles()
should avoid testing them if it can. You should only test the code inside FindAllActiveGamesFiles()
if at all possible.
Next, the test for FindAllActiveGamesFiles()
needs to set expectations for mockRepository
. Assuming Find()
and WhereIsActive()
both return the same repository you will set the mock as the return value for those methods.
I don't know the API for your mocking library, but this is how it would look in 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);
As you can see, the test code is more complicated than the code being tested. This is a good sign that you might not need the test.
精彩评论