UrbanPro
true

Learn Automation Testing from the Best Tutors

  • Affordable fees
  • 1-1 or Group class
  • Flexible Timings
  • Verified Tutors

Search in

Unit Testing MVC Web Application using MSTest with MOQ Framework

Kunal Sehgal
27/04/2017 0 0

Introduction - MSTest and MOQ framework

We can use MSTest for end to end unit testing of any managed code(.net based languages) base testing.

Mostly our applications are designed to have multiple physical tiers or logical layers like UI/BL/DL etc.

We can create unit tests to test all the layers together in which case each test case will call all the layers and methods as followed by true users and we can test all layers at once. This approach is ok but in most cases it leads to lot of dependencies for testing like having correct data  in databases, IO always to be available etc. Also it leads to in-effective testing as developers have to focus on many items at once rather than making a unit of code fully functional.

As name suggest unit testting is unit testing and shouldn't ideally be integration testing , its always better approach to use some kind of framework or tools which helps us in stubbing/faking/mocking the dependencies on other layers and focus only on the layer we want to test.

There are many tools available for this like MOQ,RhinoMOQ, MSFake and many others which helps us in mocking the dependencies and focus only on the unit of code we want to test.
All the above frameworks work on fundamental of dynamic Proxy creations.

For our purpose we are using MOQ which is the most widely used mock framework in whole industry primarily because of its effectiveness and easily understandable logic.
To use MOQ there are few considerations which includes :
a. We cant directly mock static or sealed classes and this includes both custom and .net based classes.
b. We can mock abstract or virtual classes and properties. Internally MOQ creates their overrides.
c. We can mock regular classes as well if we want to.

As a best practice all our layers/tiers should not be tightly coupled with each other and design should focus on segragating them.
Each layer should call other layer loosly which is easily possible by implementing the interfaces and not using concreate classes directly.
Using interface based implementation approach directly helps in mocking the dependencies very effectively.

Below test methods show case many different areas where mocking is important.
This solution doesn't include all possible scenerios but it's an effort to provide developers with enough knowledge on mocking and
helping them understand why its important.

Please refer other resources on code project for further knowledge on mstest fundamentals and moq.

 

Classes shown here are of a sample MVC web Project.

  • Few custom methods have been added like AddProduct, GetProduct etc for sample business rules  along with simple Action methods returning Views.
  • Action Filters have been added for testing purpose.
  • Custom Exceptions added for testing for expected Exceptions in test code.

Scenarios and items included in Test project are:

  1. Mocking standard dependencies of calling another layer from UI like Business layer/ 3rd  party/external calls.
  2. Handling static class in unit test. Static classes can’t be mocked directly using MOQ.
  3. Mocking .net framework based classes like HttpContext, ControllerContext, Principal/User and Identity. – For testing authentication scenarios.
  4. Handling Session variables by mocking SessionStateBase class. – Cache can/should also be handled same way but not tried.
  5. Handling Cookies from unit tests.
  6. Testing Action Filters (MVC).
  7. Using CSV file for data driven tests. – We can multiple data items to a unit test to check for all possible data scenarios.
  8. Detailed code commenting  of web and test projects to make code clear and  self-explanatory for developers.
  9. Comments detailing MOQ framework itself.
  10. MSTest Assert statements to test various scenarios.
  11. How to handle Exceptions in unit tests as well as exception messages.

A brief description of how to use the article or code. The class names, the methods and properties, any tricks or tips.

Blocks of code should be set as style "Formatted" like this:

// Classes defined here are MVC Controller class and ControllerTest class. It also includes code for
//defined ActionFilter as well.

//Controller name is HomeController.cs and will be part of Controller folder in MVC web app.

//ControllerTest class is HomeControllerTest.cs and will be part of Test project.

//ActionFilter is AuthorizeFilterAttribute.cs and can be placed anywhere in MVC Web project.

//We can see many other items in HomeController.cs like some interfaces defined in contructor but that is upto the dev. team to decide how they want to handle BAL or any other 3rd party dependencies and is not in scope of this tutorial.

//below code when talks about BL it means Business layer and DL means Data Acces layer.

////////////Code for Web Project's HomeController.cs class below////////////////////

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using BAL;
using MSFakes_TestApplication.Models;
using MSFakes_TestApplication.App_Start;
namespace MSFakes_TestApplication.Controllers
{
/// <summary>
/// THIS CONTROLLER CLASS HAS FEW ACTION METHODS RETURNING THE VIEWS BACK -- PART OF MVC TEMPLATE.
/// INDEX,ABOUT AND CONTACT ARE PART OF MVC TEMPLATE PROJECT.
///
/// ADDPRODUCT(GET), ADDPRODUCT(POST) AND GETPRODUCT ARE CUSTOM METHODS ADDED TO THIS CONTROLLER.
/// SOME SIMPLE BUSINESS RULES HAVE BEEN DEFINED IN THESE CUSTOM METHODS LIKE
/// CHECKING FOR SESSION VARIABLES,
/// CALLING BL TO ADD AND GET PRODUCT,
/// CHECKING IF PRODUCT AVAILABLE OR NOT,
/// CHCKING IF PRODUCT ID IS NULL,
/// ALSO CHECKING IF CORRECT VIEWS ARE RETURNED.
/// USING STATIC CLASSES IN A WAY THAT THEY ARE WRAPPED INSIDE A REGULAR INSTANCE CLASS. THIS HELPS IN HANDLING STATIC ITEMS DURING MOCKING
/// ALSO HAVE DEFINED SOME DUMMY CUSTOM EXCEPTIONS.
/// DEFINED ONE ACTION FILTER TO CHECK FOR AUTHENTICATION ETC
/// </summary>
public class HomeController : Controller
{
public HomeController()
{

}

//unit has been setup to initialize the classes for these interfaces.
private IProduct _productBL;
private IStaticFieldsHandler _staticHandler;

/// <summary>
/// defining controller constructor.
/// </summary>
/// <param name="productBL">ProductBL (BAL) implements IProduct interface</param>
/// <param name="staticHandler">in BAL there is a class wrapping the static class and implements IStaticFieldsHandler interface</param>
public HomeController(IProduct productBL, IStaticFieldsHandler staticHandler)
{
_productBL = productBL;
_staticHandler = staticHandler;
}

[AuthorizeFilter(role: "Admin")]
public ActionResult Index()
{
var vw = View("Index");
return vw;
}

public ActionResult About()
{
ViewBag.Message = "Your application description page.";

return View();
}

public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";

return View();
}

[HttpGet]
public ActionResult AddProduct()
{
//some logic
if (this.ControllerContext.HttpContext.Request.Cookies["MyBasket"] != null)
throw new CookieFoundException("Product already available in basket. Please check out");
else
{
int returnValue = _staticHandler.GetKeyValueFromDatabase("testkey");
if (returnValue == -1)
{
return RedirectToAction("Index");

}
else
return View(new ProductViewModel());
}
}

[HttpPost]
public ActionResult AddProduct(ProductViewModel model)
{

if (model.ProductID == string.Empty || model.ProductName == string.Empty || model.ProductCost == int.MinValue)
throw new ArgumentNullException("Please enter product details correctly");

Product p = new Product { ProductID = model.ProductID, ProductAvailable = model.ProductAvailable, ProductName = model.ProductName, ProductCost = model.ProductCost };
p = _productBL.AddProduct(p);

//p.Added += Product_Added;
model.ProductID = p.ProductID;

ListModel.AddModel(model);

return View("ListProducts", ListModel.Collection);
}

//public void Product_Added(object sender, EventArgs e)
//{
// ViewBag.Message = "Product Pushed";
//}

[HttpGet]
public ActionResult GetProduct(string productid = "")
{
string sessionkey = string.Empty;
//get something from session variables
if (Session["SessionVariable"] != null)
sessionkey = Session["SessionVariable"].ToString();
else
sessionkey = "No session";

if (this.ControllerContext.HttpContext.Request.Cookies["LoginCookie"] != null)
{
if (productid == string.Empty)
{
throw new ArgumentNullException("ProductId is null");
}

var p = _productBL.GetProduct(productid);
if (p.ProductAvailable == true)
{
ProductViewModel model = new ProductViewModel { ProductID = p.ProductID, ProductCost = p.ProductCost, ProductName = p.ProductName, ProductAvailable = p.ProductAvailable };

return View("Products", model);
}
else
{
ViewBag.Message = "Product Not available";
ProductViewModel model = new ProductViewModel { ProductID = string.Empty, ProductCost = int.MinValue, ProductName = "NA", ProductAvailable = false };
return View("Products", model);
}
}
else
throw new LoginCookieNotFoundException();
}
}
}

 


////////////ActionFilter - Web project's AuthorizeFilterAttribute.cs below///////////////

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MSFakes_TestApplication.App_Start
{
public class AuthorizeFilterAttribute : ActionFilterAttribute
{

string _role = string.Empty;
public AuthorizeFilterAttribute(string role)
{
_role = role;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
{
if (_role != "Admin")
{
filterContext.Result = new RedirectResult("////Home//AddProduct", false);
}
}
else
{
throw new UnauthorizedAccessException("User not authenticated to access th site");
}

base.OnActionExecuting(filterContext);
}

}
}

 

We need to have MOQ assembly either using Nuget package installed in test project before using MOQ library.

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MSFakes_TestApplication;
using MSFakes_TestApplication.Controllers;
using BAL;
using Moq;
using System.Web;
using System.Security.Principal;
using MSFakes_TestApplication.App_Start;
using MSFakes_TestApplication.Models;
using System.Diagnostics.CodeAnalysis;

//wrapper namespace for Test class

/*
* This Test class provided enough base understanding on how to create/manage unit tests using MSTest/Microsoft Unit testing tools.
* We can use MSTest for end to end unit testing of any managed code(.net based languages) base testing.
* Mostly our applications are designed to have multiple physical tiers or logical layers like UI/BL/DL etc.
* We can create unit tests to test all the layers together in which case each test case will call all the layers and methods as followed by true users and we can test all layers at once.
* Above approach is ok but in most cases it leads to lot of dependencies for testing like having correct data in databases, IO always to be available etc.
* Also it leads to in-effective testing as developers have to focus on many items at once rather than making a unit of code fully functional.
*
* As name suggest unit testting is unit testing and shouldn't ideally be integration testing , its always better approach to use some kind of
* framework or tools which helps us in stubbing/faking/mocking the dependencies on other layers and focus only on the layer we want to test.
* There are many tools available for this like MOQ,RhinoMOQ, MSFake and many others which helps us in mocking the dependencies and focus only on the unit of code we want to test.
* All the above frameworks work on fundamental of dynamic Proxy creations.
*
* For our purpose we are using MOQ which is the most widely used mock framework in whole industry primarily because of its effectiveness and easily understandable logic.
* To use MOQ there are few considerations which includes :
* a. We cant directly mock static or sealed classes and this includes both custom and .net based classes.
* b. We can mock abstract or virtual classes and properties. Internally MOQ creates their overrides.
* c. We can mock regular classes as well if we want to.
*
* As a best practice all our layers/tiers should not be tightly coupled with each other and design should focus on segragating them.
* Each layer should call other layer loosly which is easily possible by implementing the interfaces and not using concreate classes directly.
* Using interface based implementation approach directly helps in mocking the dependencies very effectively.
*
* BELOW TEST METHODS SHOW CASE MANY DIFFERENT AREAS WHERE MOCKING IS IMPORTANT.
* THIS SOLUTION DOESN'T INCLUDE ALL POSSIBLE SCENERIOS BUT IT'S AN EFFORT TO PROVIDE DEVELOPERS WITH ENOUGH KNOWLEDGE ON MOCKING AND
* HELPING THEM UNDERSTAND WHY ITS IMPORTANT.
*
* PLEASE REFER ONLINE RESOURCES FOR FURTHER KNOWLEDGE ON MSTEST FUNDAMENTALS AND MOQ.
*/
namespace MSFakes_TestApplication.Tests.Controllers
{
/// <summary>
/// Test class for HomeController.cs - using MSTest with MOQ as isolator for single layer unit testing
/// </summary>
///
//Class has to use the below attribute in order to be picked by MSTest
[TestClass]
//Just for reference this attribuet has been set here. If VS is run to calculate code coverage we are telling it to ignore the test class
[ExcludeFromCodeCoverage]
public class HomeControllerTest
{
private string _propX;

// [DeploymentItem("//TestData/BadProductData.csv")] - to Deploy test related files like data files/config files etc we can deplo them to
//either specific folder or to output folder in the configured testResult folder.

//This is called before each Test method. We can use this to some initializations like setting up default test Data or setting up some TestProperty e

0 Dislike
Follow 0

Please Enter a comment

Submit

Other Lessons for You

Selenium WebDriver 3.0, Test NG and JAVA.
Hello, Selenium WebDriver 3.0 with GeckoDriver is the latest upgrade of Selenium. To execute automation test scripts in Mozilla browser we need to install GeckoDriver seperately and the scripts should...

What Is The Difference Between Driver.close() And Driver.quit()?
• Driver.close(): It is used to close the browser or page currently in focus. close() is a webdriver command which closes the browser window which is currently in focus. During the automation process,...

Selenium waits
In Selenium web driver, the biggest challenge is to synchronize your script with the Web application under test. To make this synchronization work, we have concept of waits. Although we can use Thread.sleep()...

Priority in TestNG
public class Priority { @Test (priority=1)public void login() {System.out.println("login");} @Testpublic void email1() {System.out.println("email1");} @Test (priority=-2)public void email2() {System.out.println("email2");} //I...
S

Sarthak C.

0 0
0

The Importance Of Software Testing
Software testing is an important part of the systems development life cycle. It has its own phase in the process and has its own specialised IT professionals. What is the importance of software testing?...
X

Looking for Automation Testing Classes?

The best tutors for Automation Testing Classes are on UrbanPro

  • Select the best Tutor
  • Book & Attend a Free Demo
  • Pay and start Learning

Learn Automation Testing with the Best Tutors

The best Tutors for Automation Testing Classes are on UrbanPro

This website uses cookies

We use cookies to improve user experience. Choose what cookies you allow us to use. You can read more about our Cookie Policy in our Privacy Policy

Accept All
Decline All

UrbanPro.com is India's largest network of most trusted tutors and institutes. Over 55 lakh students rely on UrbanPro.com, to fulfill their learning requirements across 1,000+ categories. Using UrbanPro.com, parents, and students can compare multiple Tutors and Institutes and choose the one that best suits their requirements. More than 7.5 lakh verified Tutors and Institutes are helping millions of students every day and growing their tutoring business on UrbanPro.com. Whether you are looking for a tutor to learn mathematics, a German language trainer to brush up your German language skills or an institute to upgrade your IT skills, we have got the best selection of Tutors and Training Institutes for you. Read more