【问题标题】:Unit Testing GetByIdAsync method单元测试 GetByIdAsync 方法
【发布时间】:2022-12-17 16:38:29
【问题描述】:

作为我网站验证的一部分,有一种方法可以使用以下方法检查 Id 是否已存在:

        private async Task<bool> IdExistsAsync(Guid id)
        {
            var model = await repository.GetByIdAsync(id);
            return model != null;
        }

并且在类的构造函数中设置了规则,指出新 ID 不能与现有 ID 匹配:

       public ValidatorBase(IHttpContextAccessor httpContextAccessor, IAsyncRepository<T> repository)
        {
            this.repository = repository;

            if (httpContextAccessor.HttpContext.Request.Method == HttpMethods.Post)
            {
                RuleFor(model => model.Id).MustAsync(async (Id, cancellation) => !await IdExistsAsync(Id))
                    .WithMessage(string.Format("{0} Id already exists.", typeof(T).Name));
            }
        }

我现在要做的是对该方法进行单元测试,它基于一个名为 Discipline Validator 的文件,该文件扩展了验证器基类。

我创建了一些模拟学科:

        private static List<Discipline> testDisciplines = new List<Discipline>()
        {
            new Discipline
            {
                Id = new Guid("00000000-0000-0000-0000-000000000001"),
                Code = "001"
            },
            new Discipline
            {
                Id = new Guid("00000000-0000-0000-0000-000000000002"),
                Code = "002"

            },

            new Discipline
            {
                Id = new Guid("00000000-0000-0000-0000-000000000003"),
                Code = "003"

            }

        };

我创建了一个这样的测试:

        public void UniqueIdCheckTest() 
        {

            Discipline disciplineToTest = new Discipline()
            {
                Id = new Guid("00000000-0000-0000-0000-000000000001")
            };

            var httpContextAccessorMock = new Mock<IHttpContextAccessor>();
            httpContextAccessorMock.Setup(h => h.HttpContext.Request.Method).Returns(HttpMethods.Post);


            var disciplineRepositoryMock = new Mock<IAsyncRepository<Discipline>>();
            disciplineRepositoryMock.Setup(t => t.GetByIdAsync(disciplineToTest.Id, default));

            var disciplineValidator = new DisciplineValidator(httpContextAccessorMock.Object, disciplineRepositoryMock.Object);

            // Act
            var result = disciplineValidator.TestValidate(disciplineToTest);

            // Assert
            result.ShouldHaveValidationErrorFor(discipline => discipline.Id)
                .WithErrorMessage("Discipline id already exists.");

        }

我遇到的问题是,我不确定如何正确设置我的 disciplineRepositoryMock 我知道:

            disciplineRepositoryMock.Setup(t => t.GetByIdAsync(disciplineToTest.Id, default));

是不正确的,应该有更多,我认为它应该是这样的:

            disciplineRepositoryMock.Setup(t => t.GetByIdAsync(disciplineToTest.Id, default))
                .ReturnsAsync(testDisciplines
                .Where(d => d.Id == disciplineToTest.Id).ToList());

但我随后收到以下错误:

“Isetup<IAsyncRepository, Task> 不包含‘ReturnsAsync’的定义......”

所以我的问题是,如何正确设置它以便它测试 GetByIdAsync 方法?我哪里出错了?

【问题讨论】:

  • 这是使用最小起订量,不是吗?
  • 我建议不要在验证步骤中进行数据库调用。
  • 是的,你是对的@Julian,我已经把它添加到标签中了
  • 你会建议什么而不是@Neil?但是就测试而言,您对如何让它发挥作用有什么建议吗?

标签: c# unit-testing moq xunit


【解决方案1】:

您必须将实际参数值放入设置表达式或It.Is... 匹配代码中。

我总是将 Returns/ReturnsAsync 与函数一起使用,例如:

(id, ct) => testDisciplines
            .Single(id => id == disciplineToTest.Id) 
// todo: return 404 status if not existing

或者我通过异步方法的 Returns() 返回任务:

(id, ct) => Task.FromResult(testDisciplines
            .Single(id => id == disciplineToTest.Id))

或者

(id, ct) => Task.Run(() => testDisciplines
            .Single(id => id == disciplineToTest.Id), ct)

我发现 ReturnsAsync 在并行测试中对我不起作用(已同步),而 ReturnsTask 对我有用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-15
    • 2014-11-08
    相关资源
    最近更新 更多