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