【问题标题】:Mock not behaving as expected when setup设置时模拟未按预期运行
【发布时间】:2021-09-20 08:20:44
【问题描述】:

我正在尝试在 AuhtorRepository 类的 xUnit-Project 中测试名称为 GetAll 的方法。

public class AuthorRepository : IAuthorRepository
{
    private readonly ISqlDb _db;
    public string Connection { get; set; }
    public AuthorRepository(ISqlDb db)
    {
        _db = db;
    }

    public async Task<List<AuthorModel>> GetAll()
    {
        string sql = "SELECT * FROM Author";
        List<AuthorModel> authors = await _db.LoadDataAsync<AuthorModel, dynamic>(sql, new { }, Connection);
        return authors; 
    }

}

在这个方法中是LoadDataAsync 方法,我将在我的测试中模拟它。

界面的结构是这样的。

public interface ISqlDb
{
    Task<List<T>> LoadDataAsync<T, U>(string sql, U parameters, string connection);
}

终于实现了测试我的xUnit-Project。

using Autofac.Extras.Moq;
using DataAccess.Library;
using Moq;
using Repository.Library.Models;
using Repository.Library.Repositories;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Xunit;

namespace RespositoryTests
{
    public partial class AuhtorRepositoryTests
    {
        [Fact]
        public async Task GetAll_ShouldWorkd()
        {
            using (var autoMock = AutoMock.GetLoose())
            {
                //Arrange
                autoMock.Mock<ISqlDb>()
                    .Setup(x => x.LoadDataAsync<AuthorModel, dynamic>("SELECT * FROM Author", new { }, " "))
                    .ReturnsAsync(GetSamples());

                //Act
                var cls = autoMock.Create<AuthorRepository>();
                cls.Connection = " ";
                var expected = GetSamples();

                var acutal = await cls.GetAll();

                //Assert                
                Assert.Equal(expected.Count, acutal.Count);

            }

        }

        private List<AuthorModel> GetSamples()
        {
            var authors = new List<AuthorModel>();
            authors.Add(new AuthorModel { Id = 1, FirstName = "first name", LastName = "lastname" });
            authors.Add(new AuthorModel { Id = 3, FirstName = "Angela", LastName = "Merkel" });
            return authors;
        }
    }
}

项目的结构如下所示: [1]:https://i.stack.imgur.com/F4TPT.png

测试失败,声明如下:

消息:System.NullReferenceException:对象引用未设置为对象的实例。

所以我希望测试应该通过。

到目前为止我尝试了什么:

-> 我用相同的代码写了一个类似的项目,唯一的区别是项目结构。每个接口和类都在同一个测试项目中,并且测试通过了。

而在我的BookApp 解决方案中ISqlDb 适合DataAccess.Library 项目。

所以看起来在模拟中 ReturnAsync 方法没有返回值 来自我的 GetSamples。

请记住

  • a) 这是我在 stackoverflow 上的第一篇文章,并且
  • b) 我尝试在 mocking 中迈出第一步......

试过了:

 //Act
//var cls = autoMock.Create<AuthorRepository>();
//cls.Connection = "";
var cls = new AuthorRepository(autoMock.Create<ISqlDb>());
cls.Connection = "";
var expected = GetSamples();

var acutal = await cls.GetAll();

//Assert                
Assert.Equal(expected.Count, acutal.Count);

【问题讨论】:

  • 如果您正在为 AuthorRepository 类编写测试,那么您需要创建该类的对象。不需要var cls = autoMock.Create&lt;AuthorRepository&gt;();...你需要做var cls = new AuthorRepository(autoMock.Create&lt;ISqlDb&gt;();
  • 感谢@Chetan 的快速回复。不幸的是,同样的例外。

标签: c# moq autofac xunit


【解决方案1】:

默认情况下,模拟返回 null,因为模拟成员的调用与设置不匹配。参考Moq Quickstart 以更好地了解如何使用起订量。

您需要在此处使用参数匹配器,因为在设置中使用的匿名 new { } 与执行测试时实际使用的引用不匹配

//...

autoMock.Mock<ISqlDb>()
    .Setup(x => x.LoadDataAsync<AuthorModel, dynamic>("SELECT * FROM Author", It.IsAny<object>(), It.IsAny<string>()))
    .ReturnsAsync(GetSamples());

//...

注意使用It.IsAny&lt;object&gt;() 允许您传入的任何内容都被允许。

通过上述更改,测试按预期通过。

【讨论】:

  • 解释得很好,完全有道理。是的,我在 mocking 中肯定有很多东西要学。感谢您分享最小起订量快速入门。
  • @sonOfLiberty85 很高兴它有帮助。这解决了您的原始问题,然后您可以将其标记为已回答。编码愉快。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-12
  • 1970-01-01
  • 2020-06-28
相关资源
最近更新 更多