Home > OS >  Learning unit testing in C#
Learning unit testing in C#

Time:01-02

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 and actual 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
    • 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)
[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

  • Related