So I'm trying to learn unit testing in C# with a sample project I found online. The project is here.
This is my first time doing unit testing so, I am still learning and have come across some errors. Here is the code I am trying to test:
IRoosterService
:
namespace MoqBusiness
{
public interface IRoosterService
{
IList<Player> GetPlayersFromRepo();
}
}
RoosterService
:
namespace MoqBusiness
{
public class RoosterService : IRoosterService
{
private readonly IPlayersRepo _players;
public RoosterService(IPlayersRepo players)
{
_players = players;
}
public IList<Player> GetPlayersFromRepo()
{
return _players.GetPlayerList();
}
}
}
IPlayersRepo
:
namespace MoqDataRepos
{
public interface IPlayersRepo
{
IList<Player> GetPlayerList();
}
}
PlayersRepo
:
namespace MoqDataRepos
{
public class PlayersRepo:IPlayersRepo
{
public IList<Player> GetPlayerList()
{
var playerList = new List<Player> {
new Player { Name = "Swaroop", Age = 28 ,PlayersClub = new Club{ ClubName = "Manchester United",CountryName = "GB",Position = 7} },
new Player { Name = "Seema", Age = 30 ,PlayersClub = new Club{ ClubName = "Manchester United",CountryName = "GB",Position = 7}},
new Player { Name = "Jay", Age = 35 ,PlayersClub = new Club{ ClubName = "Arsenal",CountryName = "GB",Position = 4}},
new Player { Name = "Don", Age = 30 ,PlayersClub = new Club{ ClubName = "Manchester City",CountryName = "GB",Position = 1}}
};
return playerList;
}
}
}
So I am trying to test this RoosterService
class to start with, using Moq and XUnit.
My thinking was I am testing to see if the GetPlayersFromRepo
method returns the players that are in PlayersRepo
- GetPlayerList
so I would first use GetPlayersFromRepo
method, then call PlayersRepo
- GetPlayerList
method and compare the two to see if they are equal.
Here is my code for this Test:
namespace MoqTests
{
public class RoosterServiceTests
{
private readonly RoosterService _service;
private readonly Mock<IPlayersRepo> playersRepoMock = new Mock<IPlayersRepo>();
public RoosterServiceTests()
{
_service = new RoosterService(playersRepoMock.Object);
}
[Fact]
public void GetPlayersFromRepo_ShouldReturnPlayers()
{
//Arrange
var expected = playersRepoMock.Setup(p => p.GetPlayerList()).Returns(new List<Player>());
//Act
var players = _service.GetPlayersFromRepo();
//Assert
Assert.Equal(players, expected);
}
}
}
However this does not work as the following error message comes up
Argument 2: cannot convert from 'Moq.Language.Flow.IReturnsResult<MoqDataRepos.IPlayersRepo>' to 'System.Collections.Generic.IEnumerable<MoqDataModel.Player>'
I don't understand why this is happening as both methods return a list so not sure why this error occurs. Since this is my first test I guess I may be setting it up wrong too? Any advice appreciated.
CodePudding user response:
First, you don't need the return value of playersRepoMock.Setup
and what it's returning is probably not what you're expecting.
Second, in your unit test, you'll have something like this:
[Fact]
public void GetPlayersFromRepo_ShouldReturnPlayers()
{
//Arrange
var expected = new List<Player>();
playersRepoMock.Setup(p => p.GetPlayerList()).Returns(expected);
//Act
var players = _service.GetPlayersFromRepo();
//Assert
Assert.Equal(expected, players);
}
CodePudding user response:
I would suggest to do the followings:
- Naming
- Use the Given When Then structure to describe the test cases
- Use the
expected
andactual
prefixes to help legibility - Use System Under Test (or in short
sut
) as a variable name for that object which is being tested
- Technical ones
- Prefer to use non-empty collections for happy path tests
- Empty collections are usually considered edge or corner cases
- Make sure that your mocked object is being called as intended (
Verify
) - Recreate your mock object(s) in every test cases to make them isolated (F.I.R.S.T. principles)
- Prefer to use non-empty collections for happy path tests
[Fact]
public void GivenAPlayerListWithASinglePlayer_WhenICallGetPlayersFromRepo_ThenItReturnsTheReposCollection()
{
//Arrange
var club = new Club { ClubName = "Manchester United", CountryName = "GB", Position = 7 };
var player = new Player { Name = "Swaroop", Age = 28 ,PlayersClub = club };
var expectedPlayers = new List<Player> { player };
var playersRepoMock = new Mock<IPlayersRepo>();
playersRepoMock
.Setup(p => p.GetPlayerList())
.Returns(expectedPlayers);
var sut = new RoosterService(playersRepoMock.Object);
//Act
var actualPlayers = sut.GetPlayersFromRepo();
//Assert
playersRepoMock.Verify(p => p.GetPlayerList(), Times.Once);
Assert.Equal(expectedPlayers, actualPlayers);
}
Depending on the fact how defensively you want to test your code you can have the following test cases:
- Make sure that no new player has been added to the list
- Make sure that no existing player has been removed from the list
- Make sure that no player or club information is altered/tampered
- Make sure that the players' order is unchanged
- etc.
P.S.: If you need to perform assertions against collection then please prefer to use the FluentAssertions