【问题标题】: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 依赖字段的引用....

标签: c# moq xunit


【解决方案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() )
  • 只需在调用方法之前完成设置。在 mock 用作构造函数参数之前或之后完成都没有关系。
猜你喜欢
  • 2019-01-30
  • 1970-01-01
  • 1970-01-01
  • 2010-11-07
  • 1970-01-01
  • 2017-11-27
  • 1970-01-01
  • 1970-01-01
  • 2016-07-23
相关资源
最近更新 更多