Test Driven Development


The TDD is a application development way which consist writing the tests before the code.

Why ’’TDD’’ ?

The TDD is an advanced technique which will drive the development and provides dependencies loose coupling. With unit testing the parts of the Code are always tested and the feasibility of the code is covered and controlled.

Pros ?

  • The UT will guaranty the good functionality of all components and parts of your application.
  • They can act as a documentation during SDLC.
  • The development based on UT'ing enforce critical analyse and conceptional.
  • Application has the tendency to be better designed, as it will be loose coupled , maintainable as we are free to refactor and decide over conception.
  • Reduce debug time.

The Test Frameworks ?

  MsTest , NUnit , xUnit , MbUnit

The Example

Here , we will begin to have an example as easy as possible to Test and MVC project.

In MVC, there are many types of test as Action, View, Route , Redirection, Binding and etc...

To begin we create a Web Application(MVC) and in the project type selection have to check the Test Project creation , so your solution will have two project , your application and your test project.

By default Visual Studio will create 3 test methods for HomeController . the references are already included so you find the using statement in top of your test class as Microsoft.VisualStudio.TestTools.UnitTesting .

All tests are marked as TestMethod(microsoft MSTest Framework) and the test class is decorated by TestClass. you can use Fixture if you are doing UT by NUnit. The Test Methods are includes and the include 3 parts as : Arrange , Act and Assert.

Prepare the Project ?

To begin going forward on this article you can download the project source in GitHub with the following link:

https://github.com/EIDIVANDI/MyTDDTestProject

The First UT:

As we mentioned above the Visual Studio have created 3 UT for HomeController by Default. the Index text looks follow:

[TestMethod]

public void Index()

{

             //Arrange

             HomeController controler = new HomeController() ;

             //Act

             ViewResult result = controller.Index() as ViewResult ;

             //Assert

            Assert.AreEqual("Index", result.ViewBag.Message);

}


you can use a varity of the static methods of Assert class, you can have a brief review in the following link:

https://msdn.microsoft.com/fr-fr/library/microsoft.visualstudio.testtools.unittesting.assert.aspx .

The most used are : AreEqual, IsNotNull, IsNull, AreNotEqual, AreNotSame, AreSame, IsFalse, IsTrue, IsInstanceOfType.

To validate the functionality of your code run the tests by going to Test Menu/RUN/All Tests, and you will find the Test Explorer with a list of all existing tests and test state as Passed or Failed .

Testing the business layer?

Add a test class

using System;

using Microsoft.VisualStudio.TestTools.UnitTesting;

using MyProject;

using MyProjectDal.Entity;

using MyProjectBLL.Persons;



	namespace MyProject.Tests.BusinessLayer

	{

	[TestClass]

	public class PersonManagementTest

	{

	[TestMethod]

	 public void GetByName_ExistingPerson_ReturnsPerson_WithDependencies()

	{

         // Arrange

         string personName = "Paul";

         // Act

         Person result = PersonManagement.GetByName(personName);

         // Assert

         Assert.IsNotNull(result); 

	}

	}

}


Note: Before Executing the application Run All the tests , the result will be failed which is marked as Red X , if you look at the down part of test explorer you will see the details of the error is caused by sql connection and the reason is the DB context of DAL that is not created yet , but imagine a production environment with lots of UT and a failure on about 20% of tests , YES, Agree with you , it's a time wasting and shocking to be faced , the problem is the coupling of dependence against the isolation principle.

using MyProjectDAL.Context;

using MyProjectDAL.Entity;

using System.Linq;



namespace MyProjectBLL.Persons

{

   public class PersonManagement

   {

       public static Person GetByName(string name)

       {

           Person p = null;

           using (var db = new PersonContext())

           {

               p = db.Persons.SingleOrDefault(x => x.PersonName.Contains(name));

           }

           return p;

       }



   }

}



Looking at the above snippet , the problem is the direct dependency with the DataBase Context.

Repository Pattern

To achieve the solution , we need to implement the most loose coupled scenario, so the following interface is created to declare the CRUD data methods.

public interface IRepository

{

	Person GetByName(string name);

}                            





Puis, nous créons une classe repository qui implémente l’interface 



public class EFRepository : IRepository

{

	public Person GetByName(string name)

	 {

Person p = null;

using (var db = new PersonContext())

{

	p = db.Persons.SingleOrDefault(x => x.PersonName.Contains(name));

}

return p;

	}

}


Advantage of this interface is the possibility to have more implementations of repository with the meme methods and different functionality(ex. Oracle, Sql, Xml and etc. )

We need to change some parts of code to be able to do the DI (Dependency injection)

public class PersonManagement

{

    private IRepository _repository;

    public PersonManagement()

   {

	    _repository = new EFRepository();

   }

   public PersonManagement(IRepository repository)

   {

   	   _repository = repository;
 
   }

}


You can do it better by the following snippet:

public class PersonManagement

{

    private IRepository _repository;

    public PersonManagement():this(new EFRepository())

    {}

    public PersonManagement(IRepository repository)

    {

	     _repository = repository;

    }

}

Now , we change the GetByName method of the PersonManagement Class to

public Person GetByName(string name)

{

     Person p = _repository.GetByName(name);

     return p;

}


Now, we need to change all the lines of code witch are using the GetByName static method of PersonManagement

Change the line

Personmanagement.GetByName();

as

PersonManagement pm= new PersonManagement();

Person p= pm.GetByName(name) ;

So, the resulting code will be as :

	[TestClass]

	public class PersonManagementTest

	{

	[TestMethod]

	 public void GetByName_ExistingPerson_ReturnsPerson()

	{

        // Arrange

        string PersonName = "Paul";

        PersonManagement pm = new PersonManagement();

        // Act

        MyProject.Models.Person result = pm.GetByName(PersonName);

        // Assert

        Assert.IsNotNull(result); // The Person exists

	}

}

But, It's not enough correct.

Improve the UT by Faking:

There are some type of Fake : Stud , shim , moq , dummy and etc…

Here , we cover the Moq, to begin we need to add the moq nuget by using Manage Nuget Package or Package Manager Console (install-package moq)

In the PersonManagementTest class add using Moq;

and change the code as is followed :

using Microsoft.VisualStudio.TestTools.UnitTesting;

using MyProjectBLL.Persons;

using MyProjectDAL.Entity;

using MyProjectBLL.Repository;

using Moq;



namespace MyProject.Tests.Business

{

	 [TestClass]

	public class PersonManagementTest

	{

            Mock<IRepository> _repository = new Mock<IRepository>();

	

	[TestMethod]

	 public void GetByName_ExistingPerson_ReturnsPerson()

	          {

	                     // Arrange

	           string personName = "Paul";

	             _Repository.Setup(x => x.GetByName(It.Is<string>(y => y == "Paul")))

	               .Returns(new Person { PersonName = "Paul" });

	                       var pm = new PersonManagement(_Repository.Object);

	           // Act

	                     var result = pm.GetByName(personName);

	           // Assert

	            Assert.IsNotNull(result); 

	              Assert.AreEqual(personName, result.PersonName);

	    }

	 }

}

Test the controller:

The controller testing contains the action results testing

using Microsoft.VisualStudio.TestTools.UnitTesting;

using MyProject.Controllers;

using System.Web.Mvc;



namespace MyProject.Tests.Controllers

{

[TestClass]

     public class PersonControllerTests

     {

          [TestMethod]

          public void Index_NoInputs_ReturnsDefaultViewResult()

          {

               // Arrange

               PersonController controller = new PersonController ();

               // Act

               ViewResult result = (ViewResult)controller.Index();

               // Assert

               Assert.IsNotNull(result);

               Assert.AreEqual("", result.ViewName);

               Assert.IsNull(result.Model);
         }

     }

}


View Model testing:

In this part the Mocking is done by the repository, the params are passed via RoutDate, and the Controller context is simulated .

       [TestMethod]

       public void Display_ExistingPerson_ReturnView()

       {

           // Arrange

           Mock<IRepository> _repository = new Mock<IRepository>();

           _repository.Setup(x => x.GetByName(It.Is<string>(y => y == "Paul")))

           .Returns(new Person { PersonName = "Paul" });

           PersonManagement pm = new PersonManagement(_repository.Object);

           PersonController controller = new PersonController(pm);

           string personName = "Paul";

           RouteData routeData = new RouteData();

           routeData.Values.Add("id", personName);

           ControllerContext context = new ControllerContext { RouteData = routeData };

           controller.ControllerContext = context;



           // Act

           ViewResult result = (ViewResult)controller.Display();



           // Assert

           Assert.IsNotNull(result);

           Assert.AreEqual("", result.ViewName);

           Assert.IsNotNull(result.Model);

           Assert.IsInstanceOfType(result.Model, typeof(Person));

           Assert.AreEqual(personName, ((Person)result.Model).PersonName);

       }

Redirection testing:

The Display action contains the redirection for the purpose of control the behavior when the entity is not found.

          [TestMethod]

          public void Display_NonExistingPerson_ReturnNotFoundView()

          {

                      // Arrange

	           string personName = "Alex";

                      Mock<IRepository> _repository = new Mock<IRepository>();

	           _repository.Setup(x => x.GetByName(It.Is<string>(y => y == "Paul")))

	           .Returns(new Person { PersonName = "Paul" });

	           PersonManagement pm = new PersonManagement(_repository.Object);

	           PersonController controller = new PersonController(pm);



	           RouteData routeData = new RouteData();

               routeData.Values.Add("id", personName);

	           ControllerContext context = new ControllerContext { RouteData = routeData };

               controller.ControllerContext = context;



	           // Act

               var result = controller.Display() as RedirectToRouteResult;



	           // Assert

               Assert.IsNotNull(result);

	           Assert.IsInstanceOfType(result, typeof(RedirectToRouteResult));

               Assert.AreEqual("NotFound", result.RouteValues["action"]);

          }


Http Codes Testion ?

The Action methods who manage a HttpCode as 404(NotFound ) return HttpStatusCodeResult as the return ActionResult, we need to change some part of Display action when the result is null.

If (model == null )

     return HttpNotFound(‘’ Person not found ’’) ;

So, the final code will be as below:

[TestMethod]

public void Display_NonExistingPerson_ReturnsHttp404()

{

// Arrange

string PersonName = "Alex";

SetControllerContext(PersonName);



// Act

var result = _Controller.NotFoundError() as HttpStatusCodeResult;



// Assert

Assert.IsNotNull(result);

Assert.IsInstanceOfType(result, typeof(HttpStatusCodeResult));

Assert.AreEqual(404, result.StatusCode);

}



Routes Testing:

The routes are one important part of MVC and route testing is considerably important and is one of the major parts.

To be able to test the routes we need to add the reference of System.Web.Routing to our project.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

using Microsoft.VisualStudio.TestTools.UnitTesting;

using Moq;

using System.Web;

using System.Web.Routing;

using System.Web.Mvc;

using MyProject;

namespace MyProject.Tests.Routes

{

[TestClass]

public class RoutesTest

{

    public RoutesTest()

    {

         // Create the routes table

         RouteCollection routes = new RouteCollection();

         RouteConfig.RegisterRoutes(routes);

    }

}

}


You see exactly the code of Application_Start of Global.asax of your MVC application in the constructor of test class.

[TestMethod]

public void DefaultRoute_HomePage_HomeControllerIndexActionOptionalId()

{

// Arrange

Mock<HttpContextBase> mockContextBase = new Mock<HttpContextBase>();



mockContextBase.Setup(x => x.Request.AppRelativeCurrentExecutionFilePath)

.Returns("~/");



// Act

RouteData routeData = routes.GetRouteData(mockContextBase.Object);



// Assert

Assert.IsNotNull(routeData);



Assert.AreEqual("Home", routeData.Values["controller"]);



Assert.AreEqual("Index", routeData.Values["action"]);



Assert.AreEqual(UrlParameter.Optional, routeData.Values["id"]);

}

Also, it is needed to test Ignored routes which are as the xsd files.

[TestMethod]

public void IgnoreRoute_AXDResource_StopRoutingHandler()

{

       // Arrange

       Mock<HttpContextBase> mockContextBase = new Mock<HttpContextBase>();



       mockContextBase.Setup(x => x.Request.AppRelativeCurrentExecutionFilePath)

.Returns("~/Resource.axd");



        // Act

        RouteData routeData = routes.GetRouteData(mockContextBase.Object);



       // Assert

       Assert.IsNotNull(routeData);

       Assert.IsInstanceOfType(routeData.RouteHandler, typeof(StopRoutingHandler));

}

Easy to understand because well segmented.

Like
Reply

To view or add a comment, sign in

More articles by Omid Eidivandi

  • Modular Software Coupling Pitfalls

    Software is basically the composition of some modular / single perspective code components, theses components…

  • DISTRIBUTED Api

    Following all discussions that I had recently with lots of SDE , SA and Tech Leaders there are some common parts they…

    1 Comment
  • Software Architecture Design Decision

    Our today world rounds around a digital core, where any part of human and business needs are available on digital…

  • Serverless & Testing

    Serverless is modern but enough complicated, there is a need to know roughly the design and its components. it's hard…

  • Serverless Error Handling

    It's a while i'm challenging Serverless where there is no permanent server or VM to manage , where all resiliency is…

  • Some AWS FaaS Unknown Points

    When interacting with lambda we got sometime tired of microservice developed on serverless and their problems , when…

  • Some Coding Optimizations

    Recently i found some interesting coding challenges in some of our solutions developed in C# , when looking at the code…

  • LoadBalancer HealthCheck

    In this article i would like to share two scenarios i was fronted with two clients i'm working with on Azure and AWS…

  • SMACS

    What is SMACS? Social, Mobility, Analytics, Cloud et Security Social Today, all social platforms such as Twitter…

  • Three tiers Applications in Cloud

    in Today's world, Mostly we talk about cloud and cloud first, we are trying to migrate all our on-premises workloads to…

Others also viewed

Explore content categories