如何为其中有另一个方法的方法编写 xUnit 测试?

How to write a xUnit test for the method which has another method inside it?

我想为 ValidatorCreateAsync 方法编写 xUnit 测试,方法如下:

public async Task<ValidationResult> ValidateCreateAsync(IJob job)
    {
        var validationResult = _validator.NullValidator(job.Payload, job.Module, job.EntityName);

        if (validationResult.ValidationResultEnum == ValidationResultEnum.Invalid)
            return validationResult;

        await _context.Jobs.InsertOneAsync(job as Job);

        return validationResult;
    }

这是我的 NullValidator 方法:

public ValidationResult NullValidator<T>(params T[] notNullEntities)
    {
        foreach (var entity in notNullEntities)
        {
            if (entity == null)
            {
                return new ValidationResult()
                {
                    ValidationResultEnum = ValidationResultEnum.Invalid,
                    ValidationResultMessageEnum = ValidationResultMessageEnum.NullProperty
                };
            }
        }

        return new ValidationResult()
        {
            ValidationResultEnum = ValidationResultEnum.Valid,
            ValidationResultMessageEnum = ValidationResultMessageEnum.NotNullFound
        };
    }

在 xUnit 测试中,如何为我的 NullValidator 方法编写设置以在下面的测试中正常工作?

public class JobStoreTest
{

    private readonly Mock<IMongoDbContext> _moqIMongoDbContext;
    private readonly Mock<IValidator> _moqIValidator;
    private readonly JobStore _jobStore;

    public JobStoreTest()
    {
        _moqIMongoDbContext = new Mock<IMongoDbContext>();
        _moqIValidator = new Mock<IValidator>();
        _jobStore = new JobStore(_moqIMongoDbContext.Object,_moqIValidator.Object);

            _moqIMongoDbContext
            .Setup(_ => _.Jobs.InsertOneAsync(
                It.IsAny<Job>(),
                It.IsAny<InsertOneOptions>(),
                It.IsAny<CancellationToken>())
            )
            .Returns((Job y, InsertOneOptions options, CancellationToken token)
                => Task.FromResult(y));

        

        //****************This code does not work here*****************
        _moqIValidator.Setup(_ => _.NullValidator(
            It.IsAny<Type[]>())
        ).Returns((Type[] y)
            => Task.FromResult(ValidationResult validation)); 

    }

    [Theory]
    [ClassData(typeof(JobClassesForTesting))]
    public async Task CreateAsync(IJob job)
    {
        var validationResult = await _jobStore.ValidateCreateAsync(job);

        ValidationResult expectedValidationResult = new ValidationResult()
        {
            ValidationResultEnum = ValidationResultEnum.Valid,
            ValidationResultMessageEnum = ValidationResultMessageEnum.NotNullFound
        };

        Assert.Equal(expectedValidationResult, validationResult);
    }
}

我希望在第一个测试中出错,其他测试应该通过。 这是我的测试场景 (JobClassesForTesting):

public IEnumerator<object[]> GetEnumerator()
    {
        

        yield return new object[]
        {
                new Job()
                {
                    Payload = null,
                    EntityName = "EntityNameTest1",
                    Module = "ModuleTest1",
                }
        };

        yield return new object[]
        {
                new Job()
                {
                    Payload = "PayloadTest2",
                    EntityName = "EntityNameTest2",
                    Module = "ModuleTest2",
                }
        };

        yield return new object[]
        {
                new Job()
                {
                    Payload = "PayloadTest3",
                    EntityName = "EntityNameTest3",
                    Module = "ModuleTest3",
                }
        };
    }

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

job.Payload, job.Module, job.EntityName都是字符串成员,所以Type[]

_moqIValidator
    .Setup(_ => _.NullValidator(It.IsAny<Type[]>()))
    .Returns((Type[] y) => Task.FromResult(ValidationResult validation)); 

是要匹配的错误类型

需要 string[].

其次,mocked 成员不是异步的,return 也不是 Task。因此,没有理由使用 Task.FromResult

进行回调

如果使用的 IValidator 实现没有依赖关系,也没有使用实际 class 的负面影响,那么真的没有必要模拟它。使用实现本身

例如

private readonly Mock<IMongoDbContext> _moqIMongoDbContext;
private readonly JobStore _jobStore;

public JobStoreTest() {
    _moqIMongoDbContext = new Mock<IMongoDbContext>();        
    _jobStore = new JobStore(_moqIMongoDbContext.Object, new Validator());

    _moqIMongoDbContext
        .Setup(_ => _.Jobs.InsertOneAsync(
            It.IsAny<Job>(),
            It.IsAny<InsertOneOptions>(),
            It.IsAny<CancellationToken>())
        )
        .Returns((Job y, InsertOneOptions options, CancellationToken token)
            => Task.FromResult(y));

}

//...

为了让所有测试都通过,需要修改测试数据以满足预期

首先更新测试数据以也包含预期结果

public class JobClassesForTesting {

    public IEnumerator<object[]> GetEnumerator() {
        var valid = new ValidationResult() {
            ValidationResultEnum = ValidationResultEnum.Valid,
            ValidationResultMessageEnum = ValidationResultMessageEnum.NotNullFound
        };

        var invalid = new ValidationResult() {
            ValidationResultEnum = ValidationResultEnum.Invalid,
            ValidationResultMessageEnum = ValidationResultMessageEnum.NullProperty
        };

        yield return new object[] {
            new Job() {
                Payload = null,
                EntityName = "EntityNameTest1",
                Module = "ModuleTest1",
            },
            invalid
        };

        yield return new object[] {
            new Job() {
                Payload = "PayloadTest2",
                EntityName = "EntityNameTest2",
                Module = "ModuleTest2",
            },
            valid
        };

        yield return new object[] {
            new Job() {
                Payload = "PayloadTest3",
                EntityName = "EntityNameTest3",
                Module = "ModuleTest3",
            },
            valid
        };
    }

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

并重构测试以将预期结果也作为断言的参数

[Theory]
[ClassData(typeof(JobClassesForTesting))]
public async Task CreateAsync(IJob job, ValidationResult expectedValidationResult) {
    //Act
    ValidationResult validationResult = await _jobStore.ValidateCreateAsync(job);
   //Assert
   Assert.Equal(expectedValidationResult, validationResult);
}