如何模拟方法和 return 由其回调设置的结果?

How to mock a method and return a result that's set by its callback?

我正在使用下面的代码模拟 UserManager

我遇到的问题是 oResult 仍然为空。但是我无法在回调之外给它一个值,因为密码只能在回调内部使用。我不能只return一个预定值(例如IdentityResult.Success),因为结果必须在运行时生成。

这三个问题很相似,但没有完全涵盖问题:

  1. (这是最接近的)

所有这些与我的情况的区别在于,在我的情况下,方法所需的输入值仅在回调中可用。

我正在使用它来测试我的控制器的行为。

如何为模拟方法(在本例中为CreateAsync())执行回调并return其结果?


Protected Function UserManagerMock(Of TUser As Db.User, TCity as Db.City)(Users As List(Of TUser)) As Mock(Of UserManager)
  Dim oManagerMock As Mock(Of UserManager)
  Dim oStoreMock As Mock(Of IUserStore(Of TUser))
  Dim oCallback As Action(Of TUser, String)
  Dim oManager As UserManager
  Dim oResult As IdentityResult
  Dim oSetup As Expression(Of Func(Of UserManager, Task(Of IdentityResult)))

  oStoreMock = New Mock(Of IUserStore(Of TUser))
  oManagerMock = New Mock(Of UserManager)(oUserStoreMock.Object)
  oManager = oUserManagerMock.Object
  oCallback = Sub(User, Password, City)
                oResult = oManager.PasswordValidator.ValidateAsync(Password).Result

                If oResult Is IdentityResult.Success Then
                  User.PasswordHash = oManager.PasswordHasher.HashPassword(Password)
                  Users.Add(User)
                End If
              End Sub

  oManager.PasswordValidator = New PasswordValidator
  oManager.UserValidator = New UserValidator(Of TUser)(oManager)

  oSetup = Function(Manager) Manager.CreateAsync(It.IsAny(Of Db.User), It.IsAny(Of String), It.IsAny(Of Db.City))
  oUserManagerMock.Setup(oSetup).ReturnsAsync(oResult).Callback(oCallback)

  Return oManagerMock
End Function

这可以通过完全取消回调来实现,而是使用

Func(Of ..., Task(Of IdentityResult))

在设置的 Returns() 调用中。

示例:

Protected Function GetUserManager(Users As List(Of Db.User)) As UserManager
  Return Me.GetUserManagerMock(Of Db.User, Db.City)(Users).Object
End Function

Private Function GetUserManagerMock(Of TUser As Db.User, TCity As Db.City)(Users As List(Of TUser)) As Mock(Of UserManager)
  Dim oManagerMock As Mock(Of UserManager)
  Dim oStoreMock As Mock(Of IUserStore(Of TUser))
  Dim oManager As UserManager
  Dim oReturn As Func(Of TUser, String, TCity, Task(Of IdentityResult))
  Dim oResult As IdentityResult
  Dim oSetup As Expression(Of Func(Of UserManager, Task(Of IdentityResult)))

  oStoreMock = New Mock(Of IUserStore(Of TUser))
  oManagerMock = New Mock(Of UserManager)(oUserStoreMock.Object)
  oManager = oUserManagerMock.Object

  oReturn = Async Function(User, Password, City)
              If City.IsNothing Then
                oResult = IdentityResult.Failed($"{NameOf(City)} is required.")
              Else
                oResult = Await oManager.UserValidator.ValidateAsync(User)

                If oResult Is IdentityResult.Success Then
                  oResult = Await oManager.PasswordValidator.ValidateAsync(Password)

                  If oResult Is IdentityResult.Success Then
                    User.PasswordHash = oManager.PasswordHasher.HashPassword(Password)
                    Users.Add(User)

                    User.CityId = City.Id
                    User.City = City

                    City.Users.Add(User)
                  End If
                End If
              End If

              Return oResult
            End Function

  oSetup = Function(Manager) Manager.CreateAsync(It.IsAny(Of Db.User), It.IsAny(Of String), It.IsAny(Of Db.City))
  oManagerMock.Setup(oSetup).Returns(oReturn)

  Return oManagerMock
End Function

这样使用:

<Fact>
Public Async Function CreateUser() As Task
  Dim oUserManager As UserManager
  Dim oResult As IdentityResult
  Dim sPassword As String
  Dim oUser as Db.User
  Dim oCity as Db.City
  
  sPassword = "P@ssw0rd!"
  oUser = New Db.User With {.FirstName = "User", .LastName = "Name", .UserName = "user.name", .Email = "username@domain.com"}
  oCity = New Db.City With {.Id = 1, .Name = "BigCity", .Code = "L69CNV5"}

  oUserManager = Me.GetUserManager(Me.Users)
  oResult = Await oUserManager.CreateAsync(oUser, sPassword, oCity)

  Assert.True(oResult.Succeeded)
End Function

来源here. Thanks again to stakx.