繁体   English   中英

学习单元测试C#

[英]Learning unit testing in C#

因此,我尝试使用我在网上找到的示例项目来学习 C# 中的单元测试。 该项目在这里

这是我第一次进行单元测试,所以我仍在学习并且遇到了一些错误。 这是我要测试的代码:

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;
        }
    }
}

因此,我尝试使用 Moq 和 XUnit 开始测试这个RoosterService class。

我的想法是我正在测试GetPlayersFromRepo方法是否返回PlayersRepo - GetPlayerList中的玩家,所以我会首先使用GetPlayersFromRepo方法,然后调用PlayersRepo - GetPlayerList方法并比较两者以查看它们是否相等。

这是我的测试代码:

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);
        }

    }
}

但是,这不起作用,因为会出现以下错误消息

参数 2:无法从“Moq.Language.Flow.IReturnsResult<MoqDataRepos.IPlayersRepo>”转换为“System.Collections.Generic.IEnumerable<MoqDataModel.Player>”

我不明白为什么会发生这种情况,因为这两种方法都返回一个列表,所以不确定为什么会发生此错误。 因为这是我的第一次测试,我想我也可能设置错了吗? 任何建议表示赞赏。

首先,您不需要playersRepoMock.Setup的返回值,它返回的内容可能不是您所期望的。

其次,在你的单元测试中,你会有这样的东西:

[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);
}

我建议执行以下操作:

  • 命名
    • 使用Given When Then 结构来描述测试用例
    • 使用expected的和actual的前缀来帮助提高易读性
    • 使用被测系统(或简称sut )作为正在测试的 object 的变量名
  • 技术类
    • 更喜欢使用非空 collections 进行快乐路径测试
    • 确保您的模拟 object 被按预期调用( Verify
    • 在每个测试用例中重新创建模拟对象,使它们相互隔离(FIR.ST 原则)
[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);
}

根据您想要测试代码的防御性程度,您可以拥有以下测试用例:

  • 确保没有新玩家被添加到列表中
  • 确保没有现有玩家已从列表中删除
  • 确保没有球员或俱乐部信息被更改/篡改
  • 确保玩家的顺序不变
  • 等等

PS:如果您需要对集合执行断言,那么请更喜欢使用FluentAssertions

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM