使用NSubstitute替换具有值的属性返回和集合

时间:2016-05-19 20:12:39

标签: c# unit-testing nsubstitute

这是IValidationResult界面

public interface IValidationResult
{
    ICollection<IValidationError> Errors { get; }
    bool IsValid { get; }

    IValidationResult Add(IValidationError error);
    IValidationResult Add(string errorMessage);
    IValidationResult Add(params IValidationResult[] validationResults);
    IValidationResult Remove(IValidationError error);
}

我有这个测试方法

[TestMethod]
public async Task ServicoDeveRetornarFalseCasoHajaErroNaCriacaoDoObjeto()
{
    // arrange
    var validation = Substitute.For<IValidationResult>();
    validation.IsValid.Returns(false);
    _contratoFactory.Build(ContratoValues.ContratoComEmpresaNula).Returns(validation);

    // act
    var result = await _contratoService.Create(ContratoValues.ContratoComEmpresaNula);

    // assert
    result.Success.Should().BeFalse();
}

&#34;验证&#34;替代是IValidationResult的模拟。 此接口实现了一个名为IsValid的属性,它返回false是另一个属性,即Collection包含任何项目。

我的测试方法必须在

时验证
_contratoFactory.Build(any) 

被调用,它会返回一个无效的IValidationResult

然后,_contratoService.Create(any)的结果应为false

我的模拟无效。

我该如何解决?

更新 - 添加创建实施

public override async Task<IServiceOperationResult> Create(Contrato entity)
{
    var result = new ServiceOperationResult();
    try
    {
        if (entity == null)
        {
            throw new ArgumentNullException(typeof(Contrato).Name);
        }

        var validation = _contratoFactory.Build(entity);
        result.Add(validation);

        if (!result.Success)
        {
            return result;
        }

        return await base.Create(entity);
    }
    catch (ArgumentNullException ex)
    {
        result.Add(ex);
    }
    catch (Exception ex)
    {
        result.Add(ex);
    }

    return result;
}

更新 - 2015年5月20日

以下是IServiceOperationResult

的实现
public class ServiceOperationResult : IServiceOperationResult
{
    public ServiceOperationResult()
    {
        Errors = new Dictionary<string, string>();   
    }

    public IDictionary<string, string> Errors { get; }

    public bool Success => !Errors.Any();

    public void Add(string errorMessage)
    {
        if (errorMessage == null || errorMessage.Trim().Length == 0)
        {
            return;
        }

        Errors.Add(Guid.NewGuid().ToString(), errorMessage);
    }

    public void Add(IValidationResult validationResult)
    {
        if (validationResult == null)
        {
            return;
        }

        foreach (var validationError in validationResult.Errors)
        {
            Add(validationError);
        }
    }

    public void Add(IValidationError validationError)
    {
        if (string.IsNullOrEmpty(validationError?.Message) || 
            string.IsNullOrWhiteSpace(validationError.Message))
        {
            return;
        }

        Errors.Add(Guid.NewGuid().ToString(), validationError.Message);
    }

    public void Add(Exception exception)
    {
        if (exception == null)
        {
            return;
        }
        Add($"{nameof(exception)} - {exception.Message}{Environment.NewLine} - {exception.StackTrace}");
    }
}

1 个答案:

答案 0 :(得分:0)

问题似乎源于ServiceOperationResult.Add(IValidationResult validationResult)

    public void Add(IValidationResult validationResult)
    {
        if (validationResult == null) { return; }

        foreach (var validationError in validationResult.Errors)
        {
            Add(validationError);
        }
    }

这是依赖于validationResult.Errors集合,但你没有将其删除。

如果您将测试修改为类似以下内容,则应通过:

        // arrange
        var validation = Substitute.For<IValidationResult>();
        validation.IsValid.Returns(false);
        validation.Errors.Returns (new List<IValidationError> ());
        validation.Errors.Add (new ValidationError { Message = "sample error" });
        _contratoFactory.Build(ContratoValues.ContratoComEmpresaNula).Returns(validation);
        // act ...

然而,从我在本案例中可以看到的情况来看,我建议不要伪造IValidationResult,而是使用真实伪造,因为实现取决于此接口实现的实际行为。

这会使测试更像:

        // arrange
        var validation = new ValidationResult>();
        validation.Errors.Add (new ValidationError { Message = "sample error" }); 
        // I'm guessing for the real class `validation.IsValid` will now return `false`
        _contratoFactory.Build(ContratoValues.ContratoComEmpresaNula).Returns(validation);
        //act...