Prerequisites
We are going to talk about MVC unit testing, so it’s must to have at least basic idea about MVC.
You should know,
- What is the purpose of MVC?
- How MVC Works?
- What are different ways of passing data between different layers of MVC application?
- How to perform navigation in MVC?
|
|
If you are new to Asp.net MVC, please try to find answers for above questions first. Click here for our learn MVC step by step series.
What we will learn?
Note
As you can see, I have broken down this article into many sub topics. Article is written considering every reader is completely new to Unit Testing. If you know any of the sub topic listed above, you can skip that and move to next sub topic J.
When we hear the word Asp.Net MVC mainly two things comes to our mind.
- Full control over HTML
- Easy to Unit Test.
If you are already familiar with basics of unit testing then directly start with “Way towards MVC Unit Testing”.
Very basic definition will be, testing of every smallest testable block of a code in an automated manner.
Still confused?
Let me make it simpler.
- In real world every application comprises of many modules. For instance consider a simple E-Commerce application may comprises of Inventory Module, Customer management Module, Order module etc.
- Every Module is made of using more than one classes.
- Every class exposes many functionalities in the form of functions.
In Unit Testing we test these functions individually in an automated manner.
What exactly mean by “Automated Manner”?
We will write one more code which will do the testing of our other code (testing of the result of a function).
To understand this, answer following question without using any tool or application. “What is the result of 996*965/5?”
3 more questions
- Do you think your answer is correct?
- If yes, how much time you took to get the result?
- Can you reuse the calculation process (which was performed in your mind), if the figure in the operation get changed?
Now just replace the manual work performed inside your mind with a calculator.
- Now you will be 100% sure about the result.
- Answer will be quick.
- Reuse the same calculator for another calculation.
Automation makes things more accurate, faster and reusable.
When we write automatic unit test cases for our code, it will become a proof which proves that our code is working. The best part is whenever we make any changes in the code, we just rerun the previously written unit test case and confirms that previous things are not broken.
Step 1. Create a class library which need to be tested with following code block.
public class CustomMaths
{
public int Add(int num1,int num2)
{
return Number1 + Number2;
}
}
Step 2. Create Unit Testing Project as Follows
Step 3. Add reference of previous class library to this newly created project
Step 4. Create Test class as follows
[TestClass]
public class TestMathsClass
{
[TestMethod]
public void TestAdd()
{
CustomMaths maths = new CustomMaths();
int result = maths.Add(6, 5);
Assert.AreEqual<int>(11, result);
}
}
Explanation – As you can see Unit Testing follows simple three steps.
- Arrange - Create objects and prepare everything needed to test functionality
- Act – Execute and get the output
- Assert – Compare final output with expected Output
Step 5. Build your solution and open test explorer window from Test > Windows > Test Explorer
Step 6. Right click Test cases and say Run Selected Tests.
Note: In the example we are using hard coded values (5 and 6) for testing but in real life scenario we will not use hard coded values rather will use some kind of data source like excel or database for input parameters and return value.
Now you know what is unit testing and how it works. Then it’s time to understand how unit test works in MVC.
Normally when we say “Logic” only thing which we think about is “Business Logic”. In real world applications we end up with writing many different types of logics.
Business Logic
Logic related to business. Example Tax calculation, order processing, conversion (from excel to pdf) etc.
Data Transformation Logic
We have Date of Birth stored in our database and we want to display age in the page. Logic which converts our business data to a data specific to view is called Data Transformation logic.
Presentation Logic
Logic which will change the presentation in the page (or any output which end user going to get) based on some value in database (or any other value), is called Presentation Logic. Example – If employee salary is greater than 50k show him in blue color or else show him in green color.
User Interaction logic
Logic will handle different user interactions like clicking of some control is called as User Interaction Logic.
Database Logic
Logic which will handle the database specific code is called Database logic. Example of such kind of logic is executing a store procedure.
The best we can do while working with traditional Web Form application is, break it into 3 (or may be more) Layer. Each layer will perform some specific logic. In this case Business logic and Database logic will be written as two separate layer (called Business layer and database layer respectively). Unit testing of these two layers will be easy because they will be written as simple class libraries (with set of classes and functions) and thus two layers can be tested in the same way we did above.
Here the problem is,
- UI is tightly coupled to code behind.
- User Interaction logic, Presentation Logic and Data Transformation logic will be written inside code behind.
- Unit testing of code behind is not possible because code behind contains Event handlers and invoking event handler as a function will be very difficult task because it mostly have two parameters called “object sender” and “EventArgs e” which cannot be generated programmatically
Thus Unit Testing of User Interaction Logic, Data Transformation logic and Presentation logic will not be possible.
Basic components of Asp.Net MVC are,
- Model – Business data and Business logic.
- View – Aspx or Razor (or may be some user defined) UI contain some visual elements.
- Controller – Handles the user Interaction logic.
1. User Interaction Logic testing
As we know,
|
• Every request in Asp.Net MVC go via Controller.
• Controller contain the user interaction logic.
• Controller is not tightly coupled with View |
|
|
Thus testing of User Interaction logic is possible in Asp.Net MVC. |
Code Demonstration
Asp.Net MVC User interaction testing or Controller testing consist of
- Testing for ViewResult
- Testing of ViewData/ ViewBag
- Testing For RedirectResult
Demo 1 – Testing for ViewResult
Let’s assume we have following action method.
public ActionResult GetView(int id)
{
if (id == 0)
{
return View("View2");
}
else
{
return View("View1");
}
}
Our task is to create a test method which will confirm whether action method generates proper ViewResult or not.
[TestMethod]
public void TestForViewWithValue0()
{
TestingController t = new TestingController();
ViewResult r = t.GetView(0) as ViewResult;
Assert.AreEqual("View2", r.ViewName);
}
[TestMethod]
public void TestForViewWithValueOtherThanZero()
{
TestingController t = new TestingController();
ViewResult r = t.GetView(1) as ViewResult;
Assert.AreEqual("View1", r.ViewName);
}
Demo 2 – Testing for ViewData/ ViewBag
Let’s assume we have following action method.
public ActionResult Action2()
{
ViewData["Name"] = "SomeName";
return View();
}
Our task is to create a test method which will confirm whether action method generates proper ViewData or not.
[TestMethod]
public void TestForViewData()
{
TestingController t = new TestingController();
ViewResult r = t.Action2() as ViewResult;
Assert.AreEqual("Sukesh", r.ViewData["Name"]);
}
Demo 3 – Testing for Redirection
Let’s assume we have following action method.
public ActionResult Details(int Id)
{
if (Id < 0)
return RedirectToAction("Index","SomeElse");
return View("Details");
}
Our task is to create a test method which will confirm whether action method is redirecting to proper location or not.
[TestMethod]
public void TestDetailsForRedirect()
{
TestingController controller = new TestingController();
var result = controller.Details(-1) as RedirectToRouteResult;
Assert.AreEqual("Index", result.RouteValues["action"]);
Assert.AreEqual("SomeElse", result.RouteValues["controller"]);
}
[TestMethod]
public void TestDetailsForViewResult()
{
TestingController controller = new TestingController();
ViewResult result1 = (ViewResult)controller.Details(2);
Assert.AreEqual("Details", result1.ViewName);
}
Note: Testing steps won’t get change here. Once the test cases are created simply build it and from test explorer windows execute the test cases as shown in “Basic example of Unit Testing”.
2. Business Logic
As we said before Business logic and Business data is a part of Model in Asp.Net MVC.
Business logic will be implemented as a simple .Net class with couple of functions. We can easily unit test it in the same way we did in “Basic example of Unit Testing”. |
|
3. Database Logic
|
In Asp.Net MVC talk nowhere clearly spoken about the database layer. We
usually make it as a separate layer which will be accessed via Business Layer.
Again at the end of the day it’s going to be a single class with couple of function.
Unit testing will be same like above. |
4. Data transformation Logic and Presentation Logic.
This is where all voice go down. These two logic decides what data need to
be displayed and how.
They are directly associated with the view. If we write such logic in View unit testing
will not be possible. |
|
Let’s look at an application which contain Data Transformation logic and Presentation logic in the view itself.
|
What we have?
public class Employee
{
public string EmployeeName { get; set; }
public string Address { get; set; }
public DateTime DateOfBirth { get; set; }
public int Salary { get; set; }
}
public class EmployeeController : Controller
{
public ActionResult Show()
{
Employee e = GetEmployee();
return View(e);
}…
}
|
- Employee Show View (Show.cshtml)
<div>
Employee Detail<br />
Employee Name : @Model.EmployeeName<br />
Address : @Model.Address
<br />
@{
int age = DateTime.Now.Year - Model.DateOfBirth.Year;
if (Model.DateOfBirth > DateTime.Now.AddYears(-age))
{
age--;
}
}
Age : @age
<br />
@if (Model.Salary > 20000)
{
<span style="color: red">Salary : @Model.Salary</span>
As you can see, we are
- Converting date of birth to age and then displaying it - data transformation logic.
- Based on the salary color of the display text is changing – presentation logic.
Such kind of tightly coupling between UI Design, Presentation logic and Data transformation logic becomes an obstacle for complete unit testing.
What’s the solution?
Solution will be logical more than technical.
We will introduce one more layer between Model and View and call it ViewModel.
Model will be identified as data and logic related to business whereas ViewModel will be identified as data and logic related to View.
There will not be any direct connection between Model and the view. View will always connect to ViewModel which will contain
- Reference to the Model
- Data Transformation logic as per requirement in the View.
- Presentation logic based on the View requirement.
So, the first step will be creating Employee View Model as follows.
public class EmployeeVM
{
public Employee emp { get; set; }
public EmployeeVM(Employee e1)
{
emp = e1;
}
public int Age
{
get
{
int age = DateTime.Now.Year - emp.DateOfBirth.Year;
if (emp.DateOfBirth > DateTime.Now.AddYears(-age))
{
age--;
}
return age;
}
}
public string SalaryColor
{
get
{
if(emp.Salary>20000)
{
return "red";
}
else
{
return "green";
}
}
}
}
Second step will be making view a strongly typed view of ViewModel class instead of Model as follows.
<div>
Employee Detail<br />
Employee Name : @Model.emp.EmployeeName<br />
Address : @Model.emp.Address <br />
Age : @Model.Age
<br />
<span style="color:@Model.SalaryColor">Salary : @Model.emp.Salary</span>
</div>
Here we are. View Model is simply a class with two properties which can be
tested using simple unit testing logic. That means our presentation logic and data
transformation logic is now testable. |
|
See the following video on View Model in ASP.NET MVC: -
Conclusion
MVC is an architectural pattern meant for solving UI level problems. We had just saw how we separate our UI from logic completely and makes our each and every logical code a testable code.Keep coding, Keep learning and Keep sharing.
Don’t forget to vote and comment.
For technical trainings on various topics like WCF, MVC, Business Intelligence, Design Patterns, WPF,
TFS and Basic fundamentals feel free to contact SukeshMarla@Gmail.com or visit www.sukesh-marla.com
For more stuff like this click here. Subscribe to article updates or follow at twitter @SukeshMarla
See 600+ above FAQ questions and answers in .NET, C#, ASP.NET, SQL, WCF, WPF, WWF, SharePoint, Design patterns, UML etc.