这个 Moq 单元测试应该(假)检索列表有啥问题?
Posted
技术标签:
【中文标题】这个 Moq 单元测试应该(假)检索列表有啥问题?【英文标题】:What is wrong with this Moq unit test that is supposed to (fake) retrieve a list?这个 Moq 单元测试应该(假)检索列表有什么问题? 【发布时间】:2022-01-19 10:08:29 【问题描述】:我正在尝试测试这种方法,该方法旨在从数据库中检索数据并将它们存储到 List 中。我制作了这个假列表,旨在通过该方法找到。但是,该方法取而代之的是检索实际数据库并返回实际数量。我错过了什么?
public class GetAllPhonesTests
private readonly Mock<IRepository<Phone>> _mockPhoneRepository;
private readonly IPhoneService _phoneService;
private readonly List<Phone> _fakePhones = new()
new Phone() Id = 1, Brand = "Herp", Type = "Police" ,
new Phone() Id = 2, Brand = "Derp", Type = "Fireman" ,
new Phone() Id = 3, Brand = "Zerp", Type = "Nurse" ,
new Phone() Id = 4, Brand = "Flurp", Type = "Doctor" ,
new Phone() Id = 5, Brand = "Terp", Type = "Teacher"
;
public GetAllPhonesTests()
_mockPhoneRepository = new Mock<IRepository<Phone>>();
_phoneService = new PhoneService(_mockPhoneRepository.Object);
[Fact]
public void Should_ReturnFullList_When_Called()
//Arrange
_mockPhoneRepository.Setup(x => x.GetRecords(It.IsAny<SqlCommand>())).Returns(_fakePhones);
//Act
List<Phone> actual = _phoneService.GetAllPhones();
//Assert
actual.Count.Should().Be(_fakePhones.Count);
来自服务的其他内容:
public List<Phone> GetAllPhones()
string query = "SELECT * FROM phones INNER JOIN Brands ON Brands.BrandID=phones.BrandId;";
using (var command = new SqlCommand(query))
return (List<Phone>)GetRecords(command);
来自 repo 的其他内容:
public IEnumerable<T> GetRecords(SqlCommand command)
SqlDataReader reader = null;
List<T> list = new();
try
command.Connection = _connection;
_connection.Open();
reader = command.ExecuteReader();
while (reader.Read())
list.Add(PopulateRecord(reader));
reader.NextResult();
if (reader.HasRows)
while (reader.Read())
GetDataCount(Convert.ToInt32(reader["Count"].ToString()));
Status(false, "");
catch (Exception ex)
Status(true, ex.Message);
finally
reader.Close();
_connection.Close();
return list;
【问题讨论】:
当您调试时,当您进入 GetAllPhones 时,在从 repo 调用 GetRecords 时是否显示使用模拟 repo? 你能展示你的 GetAllPhones() 方法的实现吗? 不,调用进入实际的仓库。 是存储库上的 GetRecords 和服务上的 GetAllPhones?因为从粘贴的代码看来,GetAllPhones 正在调用自身的方法。那么,GetAllPhones 在存储库中吗? 您需要给我们一个minimal reproducible example。在PhoneService
的构造函数中以某种方式使用了Repository
,但您显示的代码实际上并未显示正在使用存储库。相反,PhoneService
似乎有自己的方法GetRecords
。我没有看到对 _repository
依赖字段的引用....
【参考方案1】:
我认为问题可能在于您的设置顺序,因为它应该在您将存储库对象添加到模拟服务之前完成。您可以尝试将其更改为:
public class GetAllPhonesTests
private readonly Mock<IRepository<Phone>> _mockPhoneRepository;
private readonly IPhoneService _phoneService;
private readonly List<Phone> _fakePhones = new()
new Phone() Id = 1, Brand = "Herp", Type = "Police" ,
new Phone() Id = 2, Brand = "Derp", Type = "Fireman" ,
new Phone() Id = 3, Brand = "Zerp", Type = "Nurse" ,
new Phone() Id = 4, Brand = "Flurp", Type = "Doctor" ,
new Phone() Id = 5, Brand = "Terp", Type = "Teacher"
;
public GetAllPhonesTests()
_mockPhoneRepository = new Mock<IRepository<Phone>>();
_mockPhoneRepository.Setup(x => x.GetRecords(It.IsAny<SqlCommand>
())).Returns(_fakePhones);
_phoneService = new PhoneService(_mockPhoneRepository.Object);
[Fact]
public void Should_ReturnFullList_When_Called()
//Act
List<Phone> actual = _phoneService.GetAllPhones();
//Assert
actual.Count.Should().Be(_fakePhones.Count);
您的服务也应该使用 DI 来获取使用的存储库实现,例如:
private readonly IRepository<Phone> _phoneRepo;
public PhoneService(IRepository<Phone> phoneRepo)
_phoneRepo = phoneRepo;
public List<Phone> GetAllPhones()
string query = "SELECT * FROM phones INNER JOIN Brands ON Brands.BrandID=phones.BrandId;";
using (var command = new SqlCommand(query))
return (List<Phone>)_phoneRepo.GetRecords(command);
【讨论】:
那更糟。看他的代码:phoneservice不使用repository,而是有一个本地的GetRecords... 不幸的是,就像我使用 sql 一样,我也(还)不允许使用 DI。感谢收看。 那么这就是问题所在,即使您正在模拟存储库,您也没有在服务中使用该实现,即使没有 DI,您仍然可以拥有如上所示的存储库并手动传递它,尝试以我上面显示的方式实现服务,看看你的测试是否有效,如果没有 DI,你必须做的是每次使用服务时提供一个存储库实例,例如: new PhoneService(new Repository以上是关于这个 Moq 单元测试应该(假)检索列表有啥问题?的主要内容,如果未能解决你的问题,请参考以下文章