Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 用mock进行单元测试。测试行为而不是实现_C#_Unit Testing_Mocking - Fatal编程技术网

C# 用mock进行单元测试。测试行为而不是实现

C# 用mock进行单元测试。测试行为而不是实现,c#,unit-testing,mocking,C#,Unit Testing,Mocking,当单元测试调用其他类的类时,我总是遇到一个问题,例如,我有一个类,它从一个电话号码创建一个新用户,然后将其保存到数据库,并向提供的号码发送SMS 就像下面提供的代码一样 public class UserRegistrationProcess : IUserRegistration { private readonly IRepository _repository; private readonly ISmsService _smsService; public Us

当单元测试调用其他类的类时,我总是遇到一个问题,例如,我有一个类,它从一个电话号码创建一个新用户,然后将其保存到数据库,并向提供的号码发送SMS

就像下面提供的代码一样

public class UserRegistrationProcess : IUserRegistration
{
    private readonly IRepository _repository;
    private readonly ISmsService _smsService;

    public UserRegistrationProcess(IRepository repository, ISmsService smsService)
    {
        _repository = repository;
        _smsService = smsService;
    }

    public void Register(string phone)
    {
        var user = new User(phone);
        _repository.Save(user);
        _smsService.Send(phone, "Welcome", "Message!");
    }
}
这是一个非常简单的类,但是您将如何着手并测试它呢

现在我在用mock,但我真的不喜欢它

    [Test]
    public void WhenRegistreringANewUser_TheNewUserIsSavedToTheDatabase()
    {
        var repository = new Mock<IRepository>();
        var smsService = new Mock<ISmsService>();
        var userRegistration = new UserRegistrationProcess(repository.Object, smsService.Object);

        var phone = "07012345678";

        userRegistration.Register(phone);
        repository.Verify(x => x.Save(It.Is<User>(user => user.Phone == phone)), Times.Once());
    }

    [Test]
    public void WhenRegistreringANewUser_ItWillSendANewSms()
    {
        var repository = new Mock<IRepository>();
        var smsService = new Mock<ISmsService>();
        var userRegistration = new UserRegistrationProcess(repository.Object, smsService.Object);

        var phone = "07012345678";

        userRegistration.Register(phone);

        smsService.Verify(x => x.Send(phone, It.IsAny<string>(), It.IsAny<string>()), Times.Once());
    }
[测试]
当注册一个Wuser_thenewwUsersAvedtheDatabase()时,公共无效
{
var repository=newmock();
var smsService=new Mock();
var userRegistration=newuserregistrationprocess(repository.Object,smsService.Object);
var phone=“07012345678”;
用户注册。注册(电话);
Verify(x=>x.Save(It.Is(user=>user.Phone==Phone)),Times.Once();
}
[测试]
注册Anewuser_ItWillSendANewSms()时公共无效
{
var repository=newmock();
var smsService=new Mock();
var userRegistration=newuserregistrationprocess(repository.Object,smsService.Object);
var phone=“07012345678”;
用户注册。注册(电话);
验证(x=>x.Send(phone,It.IsAny(),It.IsAny()),Times.Once());
}
感觉我在这里测试错误的东西


有什么想法可以让它变得更好吗?

在你的情况下,不需要写

  repository.Verify(x => x.Save(It.Is<User>(user => user.Phone == phone)), Times.Once());

同样对于smsService来说,使用Moq也是一种很好的方法。

重构后再看

 Mock<IRepository<>> repository;
    private Mock<ISmsService> smsService;
    const string phone = "0768524440";
    [SetUp]
    public void SetUp()
    {
           repository = new Mock<IRepository<>>();
         smsService = new Mock<ISmsService>();
    }
    [Test]
    public void WhenRegistreringANewUser_TheNewUserIsSavedToTheDatabase()
    {

        var userRegistration = new UserRegistrationProcess(repository.Object, smsService.Object);

        userRegistration.Register(phone);

        repository.VerifyAll();
        smsService.VerifyAll();
    }
Mock库;
私人模拟SMS服务;
const string phone=“0768524440”;
[设置]
公共作废设置()
{
repository=newmock();
smsService=newmock();
}
[测试]
当注册一个Wuser_thenewwUsersAvedtheDatabase()时,公共无效
{
var userRegistration=newuserregistrationprocess(repository.Object,smsService.Object);
用户注册。注册(电话);
repository.VerifyAll();
smsService.VerifyAll();
}

按照@Serghei建议的方式重构模拟是好的

我还发现行为的名称实际上并没有描述行为。我喜欢用“应该”这个词,比如“我的班级
应该做一些事情”

在注册用户时,类不应将用户发送到数据库。它应该要求存储库保存用户。这就是全部。它不知道存储库是将其发送到数据库、将其保存在内存中还是将其从轨道中删除。这不是你们班的责任

通过用这种方式表达行为,您可以明确地显示——并帮助其他人理解——您的类的职责范围结束于何处


如果您在注册新用户时将方法重命名为
。\u AsksRepositoryToSaveIt()
,这可能会使您给出的示例更自然。

您需要使用
mock.Setup(…)
来实现该功能。上下文模式、设置预期、事件、结果,然后去验证并阅读您之前设置的内容。。。当你只是试图描述你想要的行为时,这并不容易理解。真的很喜欢你在这里用词的方式谢谢。我是一个铁杆BDDer;)
 Mock<IRepository<>> repository;
    private Mock<ISmsService> smsService;
    const string phone = "0768524440";
    [SetUp]
    public void SetUp()
    {
           repository = new Mock<IRepository<>>();
         smsService = new Mock<ISmsService>();
    }
    [Test]
    public void WhenRegistreringANewUser_TheNewUserIsSavedToTheDatabase()
    {

        var userRegistration = new UserRegistrationProcess(repository.Object, smsService.Object);

        userRegistration.Register(phone);

        repository.VerifyAll();
        smsService.VerifyAll();
    }