所以我试着用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;
}
}
}
所以我试图测试这个RoosterService
类开始,使用Moq和XUnit。
我的想法是我正在测试,看看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.com flow . ireturnnsresult
'转换为'System.Collections.Generic.IEnumerable '
我不明白为什么会发生这种情况,因为这两个方法都返回一个列表,所以不确定为什么会发生此错误。因为这是我的第一次测试,我想我可能设置错了吗?如有任何建议,不胜感激。
首先,您不需要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
前缀来帮助可读性 - 使用System Under Test(或简称
sut
)作为被测试对象的变量名
- 技术的
- 对于正常路径测试更倾向于使用非空集合
- 空集合通常被认为是边缘或边缘情况
- 确保你的模拟对象被按预期调用(
Verify
) - 在每个测试用例中重新创建模拟对象,使它们相互隔离(F.I.R.S.T.原则)
- 对于正常路径测试更倾向于使用非空集合
[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);
}
根据你想要测试你的代码的防御程度,你可以有以下测试用例:
- 确保没有新玩家被添加到列表
- 确保没有现有的玩家被从列表中移除
- 确保没有球员或俱乐部的信息被更改/篡改
- 确保玩家的订单不变
- 等。
注::如果你需要对集合执行断言,那么请使用FluentAssertions