Click here to Skip to main content
15,867,686 members
Articles / DevOps / Unit Testing

MVC Unit Testing Unleashed

Rate me:
Please Sign up or sign in to vote.
4.79/5 (95 votes)
14 May 2014CPOL9 min read 223.5K   6.3K   98   57
In this article we will learn how to perform Unit Testing in Asp.Net MVC

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?

Image 1

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.

Basics of Unit Testing

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”.

What is 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.

Image 2

  • 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).

What is the importance of Unit Testing?

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.

Basic example of Unit Testing

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

Image 3

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()
    {
        //Arrange
        CustomMaths maths = new CustomMaths();

        //Act
        int result = maths.Add(6, 5);

        //Assert
        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.

Image 4

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.

Way towards MVC Unit Testing

Now you know what is unit testing and how it works. Then it’s time to understand how unit test works in MVC.

What are different types of logic we write?

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.

Image 5

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.

Difficulties in testing traditional Web Forms apps.

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

Image 6

Thus Unit Testing of User Interaction Logic, Data Transformation logic and Presentation logic will not be possible.

Testing in MVC?

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

Image 7

Image 8 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.

//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.

//Test Cases
[TestMethod]
public void TestForViewWithValue0()
{
    //Arrange
    TestingController t = new TestingController();

    //Act
    ViewResult r = t.GetView(0) as ViewResult;
            
    //Asert
    Assert.AreEqual("View2", r.ViewName);
          
}

[TestMethod]
public void TestForViewWithValueOtherThanZero()
{
    //Arrange
    TestingController t = new TestingController();

    //Act
    ViewResult r = t.GetView(1) as ViewResult;
            
    //Asert
    Assert.AreEqual("View1", r.ViewName);
          
}

Demo 2 – Testing for ViewData/ ViewBag

Let’s assume we have following action method.

//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.

//Test Cases
[TestMethod]
public void TestForViewData()
{
    //Arrange
    TestingController t = new TestingController();

    //Act
    ViewResult r = t.Action2() as ViewResult;


    //Asert
    Assert.AreEqual("Sukesh", r.ViewData["Name"]);

}

Demo 3 – Testing for Redirection

Let’s assume we have following action method.

//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.

//Test Cases
[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”.

Image 9

3. Database Logic

Image 10

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.

Image 11

Let’s look at an application which contain Data Transformation logic and Presentation logic in the view itself.

Image 12 What we have?
  • Employee Model
public class Employee
{
    public string EmployeeName { get; set; }
    public string Address { get; set; }
    public DateTime DateOfBirth { get; set; }
    public int Salary { get; set; }
}
  • Employee Controller
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>
    }
    else
    {
        <span style="color: green">Salary : @Model.Salary</span>
    }
</div>

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

  1. Reference to the Model
  2. Data Transformation logic as per requirement in the View.
  3. 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.

Image 13

See the following video on View Model in ASP.NET MVC: -

Image 14

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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Founder Just Compile
India India
Learning is fun but teaching is awesome.

Who I am? Trainer + consultant + Developer/Architect + Director of Just Compile

My Company - Just Compile

I can be seen in, @sukeshmarla or Facebook

Comments and Discussions

 
AnswerRe: Unit testing Private method Pin
Marla Sukesh15-May-14 17:19
professional Marla Sukesh15-May-14 17:19 
QuestionGreat Notes Pin
koolprasad200315-May-14 2:09
professionalkoolprasad200315-May-14 2:09 
AnswerRe: Great Notes Pin
Marla Sukesh15-May-14 4:47
professional Marla Sukesh15-May-14 4:47 
GeneralMy vote of 5 Pin
Renju Vinod14-May-14 21:27
professionalRenju Vinod14-May-14 21:27 
GeneralRe: My vote of 5 Pin
Marla Sukesh15-May-14 4:46
professional Marla Sukesh15-May-14 4:46 
GeneralMy vote of 4 Pin
Akiii_Lethal11-May-14 20:06
Akiii_Lethal11-May-14 20:06 
GeneralRe: My vote of 4 Pin
Marla Sukesh13-May-14 17:35
professional Marla Sukesh13-May-14 17:35 
GeneralRe: My vote of 4 Pin
Marla Sukesh14-May-14 16:55
professional Marla Sukesh14-May-14 16:55 
Questionvery nice Pin
BillW339-May-14 9:31
professionalBillW339-May-14 9:31 
AnswerRe: very nice Pin
Marla Sukesh9-May-14 20:29
professional Marla Sukesh9-May-14 20:29 
GeneralMy vote of 5 Pin
Member 108057699-May-14 3:49
Member 108057699-May-14 3:49 
GeneralRe: My vote of 5 Pin
Marla Sukesh9-May-14 20:27
professional Marla Sukesh9-May-14 20:27 
QuestionGood One Pin
Member 108057698-May-14 19:47
Member 108057698-May-14 19:47 
AnswerRe: Good One Pin
Marla Sukesh8-May-14 19:49
professional Marla Sukesh8-May-14 19:49 
Generalgood work Pin
ashok kusampudi k5-May-14 9:30
ashok kusampudi k5-May-14 9:30 
GeneralRe: good work Pin
Marla Sukesh5-May-14 16:49
professional Marla Sukesh5-May-14 16:49 
GeneralRe: good work Pin
Member 1023952719-Jan-15 6:44
Member 1023952719-Jan-15 6:44 
GeneralGood one Pin
Omar Gameel Salem4-May-14 13:51
professionalOmar Gameel Salem4-May-14 13:51 
GeneralRe: Good one Pin
Marla Sukesh4-May-14 17:28
professional Marla Sukesh4-May-14 17:28 
GeneralExcellent Article Pin
Member 45800921-May-14 2:14
Member 45800921-May-14 2:14 
GeneralRe: Excellent Article Pin
Marla Sukesh1-May-14 5:23
professional Marla Sukesh1-May-14 5:23 
Question. Pin
Member 1037574427-Apr-14 9:14
Member 1037574427-Apr-14 9:14 
AnswerRe: . Pin
Marla Sukesh27-Apr-14 16:20
professional Marla Sukesh27-Apr-14 16:20 
GeneralGreat Article! Pin
arinpcssouth27-Apr-14 8:12
arinpcssouth27-Apr-14 8:12 
GeneralRe: Great Article! Pin
Marla Sukesh27-Apr-14 16:20
professional Marla Sukesh27-Apr-14 16:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.