使用ArgumentCaptor验证参数

在前面我们也有看过类似如下的代码。这是一个基本类型的参数,我们可以方便的直接写在Verify里面。

settingService.Verify(it => it.GetStudentSetting("ex"), Times.Once);

但是如果是一个自定义的对象,我们也是可以使用It.Is来验证参数

 public class StudentController
    {
        private readonly IStudentService studentService;
        public StudentController(IStudentService studentService)
        {
            this.studentService = studentService;
        }

        public void Add(StudentModel studentModel)
        {
            var student = new Student()
            {
                Birthday = studentModel.Birthday,
                Name = studentModel.Name
            };
            //还有其它的代码等.
            studentService.Add(student);
        }
    }

    public interface IStudentService
    {
        void Add(Student student);
    }

    public class StudentModel
    {
        public DateTime Birthday { get; set; }
        public string Name { get; set; }
    }

    public class Student
    {
        public DateTime Birthday { get; set; }
        public string Name { get; set; }
    }

    [Fact]
    public void Add_Should_Invoke_Service_To_Add()
    {
        var service = new Mock<IStudentService>();
        var controller = new StudentController(service.Object);

        var model = new StudentModel();

        controller.Add(model);

        service.Verify(it => it.Add(It.Is<Student>(x => x.Name == model.Name && x.Birthday == model.Birthday)), Times.Once);
    }

上面的有一个缺点就是如果属性很多的话我们写起来会比较麻烦。然后也不清楚具体是哪一个参数没有传对。 如果student的参数比较多的话,这个也会变得不易读了。

ArgumentCaptor 参数捕获器

using Moq;
using System.Collections.Generic;

//我们故意将命名空间设成这个。因为这个是每个单元测试都要用的。
namespace Xunit
{
    public class ArgumentCaptor<T>
    {
        public T Value { get; private set; }

        public List<T> Values { get; private set; } = new List<T>();

        public T Capture()
        {
            return It.Is<T>(t => SaveValue(t));
        }

        private bool SaveValue(T t)
        {
            Value = t;
            Values.Add(t);
            return true;
        }
    }
}

为了能方便的使用FluentAssertions.我们设计了一个类叫 ArgumentCaptor 然后上面的单元测试代码就可以变成如下的形式了

[Fact]
public void Add_Should_Invoke_Service_To_Add()
{
    var service = new Mock<IStudentService>();
    var controller = new StudentController(service.Object);

    var model = new StudentModel();

    controller.Add(model);

    var argStudent = new ArgumentCaptor<Student>();
    service.Verify(it => it.Add(argStudent.Capture()), Times.Once);
    argStudent.Value.Name.Should().Be(model.Name);
    argStudent.Value.Birthday.Should().Be(model.Birthday);
}

代码是变多了。但是更易读了。

还有一个比较方面的是写在Callback里面。 但是跟我们UnitTest的风格不太符合了。

  1. Arrange
  2. Action
  3. Assertion
下一篇:Mock dbcontext
最近更新的
...