Sample 1: Faking HttpContext and ModelState Site
Original Method
The following method was taken from visual studio Template for ASP.NET Web Application using MVC
The following sample includes a function that receive a LoginViewModel and check's if the Model and the password are valid, then it returns a ViewResult that contains the state of the login request.
C# [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task<ActionResult> Login(LoginViewModel model, string returnUrl) { if (!ModelState.IsValid) { return View(model); } var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false); switch (result) { case SignInStatus.Success: return RedirectToLocal(returnUrl); case SignInStatus.LockedOut: return View("Lockout"); case SignInStatus.RequiresVerification: return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe }); case SignInStatus.Failure: default: ModelState.AddModelError("", "Invalid login attempt."); return View(model); } }
VB
<HttpPost>
<AllowAnonymous>
<ValidateAntiForgeryToken>
Public Async Function Login(model As LoginViewModel, returnUrl As String) As Task(Of ActionResult)
If Not ModelState.IsValid Then
Return View(model)
End If
Dim result = Await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout := False)
Select Case result
Case SignInStatus.Success
Return RedirectToLocal(returnUrl)
Case SignInStatus.LockedOut
Return View("Lockout")
Case SignInStatus.RequiresVerification
Return RedirectToAction("SendCode", New With {
.ReturnUrl = returnUrl,
.RememberMe = model.RememberMe
})
Case Else
ModelState.AddModelError("", "Invalid login attempt.")
Return View(model)
End Select
End Function
Typemock Isolator Features Used
• Recursive Fakes
• Ignore methods
• Mock HttpContext and fake it's Extension Method
• Mock async method
Scenario
1. Create LoginViewModel to use as an argument to the under test method, AccountController.Login.
2. Fake AccountController.ModelState property to be valid and ignore the method AccountController.ModelState.AddModelError.
3. Fake AccountController.HttpContext.GetOwinContext.Get<ApplicationSignInManager> so we can return a fake ApplicationSignInManager.
4. Fake ApplicationSignInManager.PasswordSignInAsync that it will return that the password check has failed.
5. Call AccountController.Loginand check the result using Assertions and Verification.
Code
C# [TestMethod, Isolated] public async Task TestWhenLoginIsBad_ErrorMessageIsShown() { // Arrange // Create the wanted controller for testing var controller = new AccountController(); var loginData = new LoginViewModel { Email = "support@typemock.com", Password = "password", RememberMe = false }; // Fake the ModelState Isolate.WhenCalled(() => controller.ModelState.IsValid).WillReturn(true); // Ignore AddModelError (should be called when login fails) Isolate.WhenCalled(() => controller.ModelState.AddModelError("", "")).IgnoreCall(); // Fake HttpContext to return a fake ApplicationSignInManager var fakeASIM = Isolate.WhenCalled(() => controller.HttpContext.GetOwinContext().Get<ApplicationSignInManager>()).ReturnRecursiveFake(); // When password checked it will fail. Note we are faking an async method Isolate.WhenCalled(() => fakeASIM.PasswordSignInAsync(null, null, true, true)).WillReturn(Task.FromResult(SignInStatus.Failure)); // Act var result = await controller.Login(loginData, "http://www.typemock.com/"); // Assert // The result contains login data, doesn’t redirect Assert.IsInstanceOfType(result, typeof(ViewResult)); Assert.AreSame(loginData, (result as ViewResult).Model); // Make sure that the code added an error Isolate.Verify.WasCalledWithExactArguments(() => controller.ModelState.AddModelError("", "Invalid login attempt.")); }
VB
<TestMethod(), Isolated()>
Public Async Function TestWhenLoginIsBad_ErrorMessageIsShown() As Task
' Arrange
' Create the wanted controller for testing
Dim controller = New AccountController()
Dim loginData = New LoginViewModel() With {
.Email = "support@typemock.com",
.Password = "password",
.RememberMe = False
}
' Fake the ModelState
Isolate.WhenCalled(Function() controller.ModelState.IsValid).WillReturn(True)
' Ignore AddModelError (should be called when login fails)
'fake.Get(Of ApplicationSignInManager)
Isolate.WhenCalled(Sub() controller.ModelState.AddModelError("", "")).IgnoreCall()
' Fake HttpContext to return a fake
Dim fakeSIM = Isolate.WhenCalled(Function() controller.HttpContext.GetOwinContext().[Get](Of ApplicationSignInManager)).ReturnRecursiveFake()
' When password checked it will fail. Note we are faking an async method
Isolate.WhenCalled(Function() fakeSIM.PasswordSignInAsync(Nothing, Nothing, True, True)).WillReturn(Task.FromResult(SignInStatus.Failure))
' Act
Dim result = Await controller.Login(loginData, "http://www.typemock.com/")
' Assert
' The result contains login data, doesn’t redirect
Assert.IsInstanceOfType(result, GetType(ViewResult))
Assert.AreSame(loginData, TryCast(result, ViewResult).Model)
' Make sure that the code added an error
Isolate.Verify.WasCalledWithExactArguments(Sub() controller.ModelState.AddModelError("", "Invalid login attempt."))
End Function