如何为其中有另一个方法的方法编写 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);
}
我想为 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);
}