C# 使用存根和模拟进行单元测试

C# 使用存根和模拟进行单元测试,c#,unit-testing,C#,Unit Testing,我正在为一些类编写测试。我要测试的类使用不同的存储库从数据库中获取和保存内容 一个简化的例子 public class MyClass { private readonly IGroupRepository _groupRepo; public MyClass(IGroupRepository groupRepo){ _groupRepo = groupRepo; } public void Execute(PersonInfo personInfo, str

我正在为一些类编写测试。我要测试的类使用不同的存储库从数据库中获取和保存内容

一个简化的例子

public class MyClass
{
    private readonly IGroupRepository _groupRepo;

  public MyClass(IGroupRepository groupRepo){ 
    _groupRepo = groupRepo; 
  }

  public void Execute(PersonInfo personInfo, string id){
  var group = _groupRepo.GetById(id);

  var person = group.Persons.First(p=> p.Id == personInfo.Id);

  person.FirstName = personInfo.FirstName;
  person.LastName = personInfo.LastName;

  _groupRepo.Save(group);      
  }
}
我想做的是确保保存到数据库中的内容是正确的

所以我有一个这样的单元测试:

[TestMethod]
public void TestMethod(){
    var groupId = "ABC";
    var personId = 1;

    ver personInfo = new PersonInfo()
    {
      Id = personId,
      FirstName = "Sam",
      LastName = "Smith"  
    }

    var groupStub = new Mock<IGroupRepository>;
    groupStub.Setup(x=> x.GetById(groupId)).Returns(new Group(){
            Id = groupId,
            Persons = List<Person>()
            {
                new Person()
                {
                    Id = personId,
                    FirstName = "George",
                    LastName = "Bolton",
                }
            }
        }
    });

    var myClass = new MyClass();
    myClass.Execute(personInfo, groupId);

    var group = groupStub.GetById(groupId);
    var person = group.Persons.First(p=> p.Id == personId);

    Assert.AreEqual(personInfo.FirstName, person.FirstName);
}
groupStub.Received().Save(
    Arg.Is<Group>(group =>
    {
        return groupId.Id == groupId && 
            group.Persons[0].Id == personId &&
            group.Persons[0].FirstName == "Sam" &&
            group.Persons[0].LastName == "Smith";
    });
[TestMethod]
公共void TestMethod(){
var groupId=“ABC”;
变量personId=1;
ver personInfo=新personInfo()
{
Id=人形,
FirstName=“山姆”,
LastName=“史密斯”
}
var groupStub=新模拟;
groupStub.Setup(x=>x.GetById(groupId)).Returns(newgroup()){
Id=groupId,
人员=名单()
{
新人()
{
Id=人形,
FirstName=“乔治”,
LastName=“博尔顿”,
}
}
}
});
var myClass=新的myClass();
Execute(personInfo,groupId);
var group=groupStub.GetById(groupId);
var person=group.Persons.First(p=>p.Id==personId);
Assert.AreEqual(personInfo.FirstName,person.FirstName);
}
我认为这很好,但后来我读了更多关于它的内容,我读到你不应该在潜艇上断言,我想我在我的断言声明中是这样做的?我从存根中获取信息,然后对结果使用断言

所以我不确定这是否是正确的测试方法。如果没有,为什么以及正确的方法是什么

不建议您测试保存到中的内容 数据库是正确的。它更多的是集成测试,而不是单元测试 测试,它超出了类的范围

可能有很多原因导致数据无法正确保存在数据库中,但您的类的行为是正常的,例如网络问题,但如果UT失败则不正确

相反,您应该测试您的类,以了解在数据库插入失败或发生异常时类的行为。然后使用您的模拟框架以这些期望来模拟存储库,并在类的测试中相应地断言


一些好的阅读资料和

您不应该测试正在实施单元测试的类中使用的另一个模块/类的逻辑

您应该只测试/验证对该模块的调用是否正在被调用,就像您的业务逻辑所说的那样。您应该只关注该类的业务逻辑,只针对您正在编写的单元测试用例

在您的案例中,IGroupRepository是另一个模块/类,而不是为其编写单元测试的模块/类。相反,如果您想验证是否保存了数据,那么您应该在为IGroupRepository实现的另一个测试用例中包含该数据


但是,不建议编写用于数据保存的单元测试,并将其视为集成测试的一部分。

是的,您正确地认为编写测试的方式有问题。事实上,您并不是在测试
MyClass
。相反,您正在测试 你使用的模拟框架是有效的。作为证明,对使用
MyClass
的两行进行注释,然后再次运行测试。它仍将通过:

[TestMethod]
public void TestMethod()
{
    var groupId = "ABC";
    var personId = 1;

    ver personInfo = new PersonInfo()
    {
      Id = personId,
      FirstName = "Sam",
      LastName = "Smith"  `
    }

    var groupStub = new Mock<IGroupRepository>;
    groupStub.Setup(x=> x.GetById(groupId)).Returns(new Group(){
            Id = groupId,
            Persons = List<Person>()
            {
                new Person()
                {
                    Id = personId,
                    FirstName = "George",
                    LastName = "Bolton",
                }
            }
        }
    });

    // var myClass = new MyClass();
    // myClass.Execute(personInfo, groupId);

    var group = groupStub.GetById(groupId);
    var person = group.Persons.First(p=> p.Id == personId);

    Assert.AreEqual(personInfo.FirstName, person.FirstName);
}
在您使用的模拟框架中应该有类似的东西