I'm using the code below to mock a UserManager
.
The problem I'm having is that oResult
remains null. But I'm unable to give it a value outside of the callback, as a password is only available inside the callback. I can't just return a predetermined value (e.g. IdentityResult.Success
), as the result must be generated at runtime.
These three questions are similar, but they don't exactly cover the issue:
The difference between all of these and my situation is that in mine the method's required input value is only available within the callback.
I'm using this to test the behaviors of my controller(s).
How can I execute a callback for a mocked method (CreateAsync()
in this case) and return its result?
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
This can be accomplished by doing away with the callback altogether, and instead using a
Func(Of ..., Task(Of IdentityResult))
in the setup's Returns()
call.
Example:
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
Use it like this:
<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