Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

What, Where, When and Who to Test in Your Code

4.96/5 (15 votes)
24 Feb 2015CPOL21 min read 21K   2  
What, where, when and who to test in your code

Introduction

"What to test? Where to test? Who to test? This is a post with practical alternatives to testing the domain layer, data access and user interface in real-world applications. Suggesting types of testing according to the layer on which it belongs, within a DDD-based architecture, in typical applications ASP.NET MVC."

In theory, a unit testing is straightforward to be built and comprehended since it seems small programs, with inputs/processing/outputs are well-defined. But the implementation of testing in "real-world applications" is much more complicated in practice than in theory. Either using unit testing throughout the application or only the most critical parts for writing specific tests. In general, often after theoretical examples are understood, it's difficult to know where to apply unit testing? What to test? And mainly, how to test our own source code?

This post aims to show some examples of tests to the layers of an MVC application with methods that access a database, user interactions, according to the DDD concepts. The DDD architectural pattern provides a framework of practices and terminology for making design decisions that focus and accelerate software projects, dealing with complicated domains (Wikipedia), with previously defined layers, Domain Layer, Application Layer, Data Access Layer and Presentation Layer (Figure 1). Each of these layers have different responsibilities and characteristics, therefore different types of unit testing.

Image 1

Figure 1: DDD Layers

Regarding the unit testing, they should be fast since they were made to test only ONE function of application in memory, in other words, only ONE logic by the test. They should have no access to classes that connect to the database, file system, network services and any other classes that need external resources. Unit testing should always pass or fail, to define a result for logic being tested. They're made separately, are self-sufficient and it can’t depend on other tests. Thus, regardless of the order that are tested should always work. Unit testing is clean, with a quick look, it is possible to understand what it sets out to do. Tests must have good names, without economy of words, regarding the standards of “good programming practices”.

Regardless of tested layer, a good practice is the creation of a Unit Test Project, copying the same folder structure of the project to be tested, as shown in Figure 2. This practice facilitates the location of a test or set of tests. Because the tested classes and the project itself will have a corresponding name, just by inserting the suffix ".test". According to this pattern, the customer.cs class (domain class example), will have a corresponding unit testing class called customer.test.cs. Both classes would be in the "Entities" folder with the names "Name_App.Domain.Core" and "Name_App.Domain.Core.Tests". It can seem unnecessary, but this simple organization will help clarify much about the tests content and its purpose to be performed.

Image 2

Figure 2: Folder Structure
 

Create file structures and test project folders according to the project to be tested.

But what to test? According to the instructions in this post, it depends on the layer, class and method that is being tested! Each layer has its responsibilities and contains classes that follow the same characteristics. A big misconception of beginners is that they don't have this vision and they want to test everything out of place. For example, isn't a responsibility of a domain service testing the DbContext access, or test whether the repository class instantiated in some domain service is writing records in the database. This would be a role of the data access layer, or even integration tests. The function of a unit test is test a method, or a functionality, or a responsibility under which some piece of code it claims to do. Often the method name itself already indicates its purpose and consequently what it takes to test.

A unit testing performs tests based in its layer, by checking what the code under test it claims to do.

There is a type of interpretive failure that occurs in the unit testing scope, when a Unit Test Project has different project references being tested. As a rule of thumb, the two projects should have the same references! Both the project being tested, as the project that performs the tests. Once it is knows the role of each layer is known how the layers communicate with each other, then it is known which layers are referenced to other. Therefore, the two projects will have the same references, except for certain specific framework for testing, or specific for dependency injection and other references that serve only to facilitate the implementation work.

Tests in Domain Model Layer

When the creation of an application is started, abstracting the entities to be used usually starts up, with implementing their classes in the domain layer. This is the way indicated by the OOP and DDD. Shouldn't starting the application construction either by the database creation or by the "screens" that the system will have. Each domain entity is implemented as class and this class has properties and methods. The unit testing focus is to test each method of each entity (let's not get into the discussion about the private methods testing). Should we test all methods? Maybe yes or not depends on the methods! But being a practical professional, not all methods need to be tested, only those with minimal complexity. Some professionals will twist the nose on this statement, but our view is the conscious use of resources, including precious hours of work of professionals.

From our point of view, this layer has the most pleasant tests to write, since they manipulate the business logic exclusively. Write unit testing for the domain entities to expose the functionality and future needs of the system, giving a complete view of application. Within the DDD approach, a domain layer doesn't uses external resource, no ORM, no framework for dependency injection, no concern with any external resource. As a rule of thumb, the domain layer contains in their references only the System namespace, which further justifies the use of their own classes and methods for their tests.

The Document class is an entity of the domain layer containing methods that manipulate their own fields and other domain entities. It has a public method AddField and two other private methods, AddFieldIfNameNotIsNullOrEmpty and AddFieldIfNotContainsDocumentField. It is important to clarify that the AddField method was tested and as a result of its testing and refactoring, arise these two private methods. One of the tasks of unit testing construction is to provide a basis for refactoring of source code. Each developer has a working method, in some cases when it isn't quite known what to do, or how to do, the tests are a good start. In this case, the unit testing also helped to determine whether it a new entity should be created, or just an array of strings to DocumentFields.

C#
public class Document : IDocument
{
    public long Id { get; set; }

    public string Name { get; set; }

    public virtual ICollection<DocumentField> DocumentFields { get; set; }

    public Document()
    {
        DocumentFields = new List<DocumentField>();
    }

    public void AddField(DocumentField documentField)
           {
        if (documentField == null){
            throw new ArgumentNullException();
        }
        else {
            AddFieldIfNameNotIsNullOrEmpty(documentField);
        }
    }

    private void AddFieldIfNameNotIsNullOrEmpty(DocumentField documentField)
    {
        if (string.IsNullOrEmpty(documentField.Name)){
            throw new ArgumentException();
        }
        else {
            AddFieldIfNotContainsDocumentField(documentField);
        }
    }

    private void AddFieldIfNotContainsDocumentField(DocumentField documentField)
           {
        if (!DocumentFields.Contains(documentField)){
            DocumentFields.Add(documentField);
        }
    }
}

Example 1

C#
[TestMethod]
public void AddFieldMethodSavesDocument()
{
    var document = new Document();
    var documentField = new DocumentField
           {
        Name = "DOCUMENT FIELD NAME"
           };
    document.AddField(documentField);

    string documentFieldName = document.DocumentFields.FirstOrDefault<DocumentField>().Name;

    Assert.AreEqual(document.DocumentFields.Count, 1);
    Assert.AreEqual(documentFieldName, "DOCUMENT FIELD NAME");
}

Example 1 is a typical example of unit testing for domain layer. In this case, the test function is verifying if the AddField method, of the Document class is adding items in its collection. The AddField method, of the Document object has an internal collection (not exposed publicly) for adding DocumentField objects. Thus, a Document object has one or more DocumentFields. The unit testing verifies whether by adding an instance of DocumentField object to the method, it really will persist the object.

Example 2

C#
[TestMethod]
public void AddFieldMethodReceivesEmptyValueForNameField()
{
    Exception caughtException = null;
    var document = new Document();
            
    try
    {
        var documentFieldFake = new DocumentField
        {
            Name = ""
        };
        document.AddField(documentFieldFake);
    }
    catch (Exception err)
    {
        caughtException = err;
    }

           Assert.IsInstanceOfType(caughtException, typeof(ArgumentException));
    Assert.AreEqual(document.DocumentFields.Count, 0);
}

In Example 2, an invalid value (empty string) is sent at the same AddField method of the Document object. According to the business rules, the Name field has a value other than empty mandatory. Thus, the AddField method should treat this clearly to the application user and should throw an exception making it clear that this type of entry is invalid. Business rules are an attribution of the domain layer, which must be tested only in the domain layer. Given this, for the other application layers, if a DocumentField belongs to DocumentFields collection, all their fields were consisted before the insertion. It is not necessary to this test again in another layer.

There are some good practices that improve the code under test, allowing validations that aren't seen in normal situations. Unconsciously developers create a code to be used for a perfect user who doesn't make mistakes and that has only good intentions. Unit tests open a better insight for the necessary validations. It's necessary to create unit tests with null values for the test method parameters. Or input invalid values as in Example 2, as empty strings, values out of range, insert a particular type instead of other and so on. When this problem type is NOT correctly handled by code, the test doesn’t compile, it generates errors, it does not perform test asserts. This is a sign that some piece of code has any part that needs an exception handling. In this case, with the help of debugging, the problem can be found and fixed. One of the tasks of unit testing is providing a basis for refactoring source code. Moreover, even by not making use of TDD, unit testing shouldn't only check the obvious, but try to find problems that have not been thought of.

Unit tests shouldn't only check the obvious, but should try to find problems that haven't been thought out and then validate them.

Example 2 test has two asserts. This practice can be regarded as a bad smell, according to the statement, one assert for each unit testing. In the case of this post, the theme suggested by the test (the test name itself) should be considered, which implies a sequence of actions that involve more than one statement. For this example, an exception was launched, then the collection DocumentFields must not be used and should be empty. Can be other actions (asserts) that have not been tested and that are also part of this theme by increasing the assertions number.

The test name says a lot, which usually treats a theme about the code, which can be treated by one or more asserts.

The domain layer also contains services within a DDD-based architecture. A domain service has those operations that don't specifically belong to Domain entities, are inherently activities or operations, not internal characteristics of Domain Entities. Within this implementation approach, the domain services can call methods of the repository. The domain layer has repository interfaces and its implementation is on data access layer.

C#
public class DepartmentService : ServiceBase<Department>, IDepartmentService
{
    private readonly IDepartmentRepository _DepartmentRepository;

        public DepartmentService(IDepartmentRepository departmentRepository)
            : base(departmentRepository)
        {
        _DepartmentRepository = departmentRepository;
        }
}

What to test in a domain service? A good test is to verify the ability of the service methods can correctly call one or more methods of the repository that are associated with it. There are other possibilities as well test involving business rules for the domain and other classes that can be associated with it. It's very important to understand that at this point, it doesn't matter whether a service method is able to fetch any data in the database, or anywhere else. This isn't a function of a domain service method, but a repository method. This test should be performed later by testing the Data Access Layer and not now. The function of a domain service method is to perform operations with domain entities and other accessible objects in this layer. The DepartmentService class has the IsASessionOfTheDepartment method containing the parameters of the long DepartmentID and sectionId type, to be tested next.

C#
public class DepartmentService
{
    private readonly IDepartmentRepository _DepartmentRepository;

    public DepartmentService(IDepartmentRepository departmentRepository)
    {
           _DepartmentRepository = departmentRepository;
    }


    public bool IsASessionOfTheDepartment(long departmentId, long sectionId)
    {
        // Other Operations.

        // Bug here!
        var departmentRepository = _DepartmentRepository.GetById(sectionId);

        // Other Operations.
    }
}

The IsASessionOfTheDepartment method internally handles the GetById method of _DepartmentRepository. An implementation error was purposely inserted by entering the sectionId parameter instead of departmentID. This is a type of error that any developer commits careless in performing their work. One purpose of the tests is to verify this type of situation. However, it will be tested if IsASessionOfTheDepartment is calling GetById correctly, as expected (Example 3).

Example 3

C#
[TestMethod]
public void  IsASessionOfTheDepartmentMethodDisplayProblemWithWrongParameters()
{
    const long sectionId = 101;
    const long departmentId = 12;

    long receivedDepartmentId = long.MinValue;

    var departmentRepositoryMock = new Mock<IDepartmentRepository>();
    departmentRepositoryMock
               .Setup(dep => dep.GetById(It.IsAny<long>()))
               .Returns(new Department())
               .Callback<long>(id => receivedDepartmentId = id);

    var departmentService = new DepartmentService( departmentRepositoryMock.Object);

    List<Department> listOfDepartmentsResult = 
        departmentService. IsASessionOfTheDepartment(departmentId, sectionId);

    Assert.AreEqual(receivedDepartmentId, departmentId); // Error, receivedDepartmentId returns 101
}

In Example 3, test by entering a value for the departmentID parameter equal to 12, should be checked if the same value is used when it is passed to GetById within IsASessionOfTheDepartment. As the IsASessionOfTheDepartment method was implemented wrong, the receivedDepartmentId variable is loaded with the wrong value. Thus, the test won't pass and you can check the error of implementation.

There are some types of objects used for tests in order to replace a resource that can't or shouldn't be accessed by a unit test at a given moment. There are objects Dummy, Fakes, Stubs and Mocks, each one with its own peculiarities and for a purpose. There is a detailed description of each of these objects in the good articles, http://www.martinfowler.com/bliki/TestDouble.html and http://martinfowler.com/articles/mocksArentStubs.html. Even without detailing on each of them, about how they work, or knowing in that situation they must be used. It's important to know that they're objects to replace other. There are many cases where a certain class needs to be tested and has one or more associated classes. If a code will be tested and it doesn't depend on any behavior of associations, then its dependencies don't need to be instantiated and can be replaced by one of these objects. If simulating some inaccessible behavior is needed, or it is required to provide some external resource that can't be accessed, then you can use one of these objects.

There are cases when a class under test has dependencies with third-party classes, web services, database access classes, file system classes and even classes that are part of our application. In such cases, it must be determined whether one of the objects to replace the feature can/should not be used. These objects can be used not only to replace a feature difficult to access, but also to facilitate the writing of unit testing, avoiding a lot of code that doesn't interfere in the test objectives.

Why use a Mock object for the Example 3 test? This question is often done at the beginning of the learning about tests. In this case, there are two reasons for the use of departmentRepositoryMock object. The first concerns the easy way to instantiate this object through the Moq framework. If the DepartmentRepository object is instantiated normally, then there is a concern with its dependencies and with the creation of other objects that DepartmentRepository is associated with. There is a need to create a succession of other dependent objects on cascade. Sometimes, it a null value parameter can be inserted that doesn't influence the test results, but this isn't always possible. The Moq Framework, as well as other frameworks, takes care of the creation of an object and its dependencies, because this is better use it. The second reason is due to ease of SIMULATING a calling mock method and SETUP a return value for that method, as in Example 4. Or even TO FETCH (using the callback) a parameter value used in some method according to Example 3.

Example 4

C#
[TestMethod]
public void  CheckIfDepartmentServicePersistDepartments()
{
    Department dep1 = new Department();
           Department dep2 = new Department();
           Department dep3 = new Department();

    var departmentRepositoryMock = new Mock<IDepartmentRepository>();
    departmentRepositoryMock
               .Setup(dep => dep.GetAll())
               .Returns(new List<Department> 
        {dep1, dep2, dep3 }); // It sets that the GetAll value is a list of DEP1, DEP2 and dep3.

    var departmentService = new DepartmentService( departmentRepositoryMock.Object);
       
    var listOfDepartments = departmentService.GetAllDepartments();

    Assert.IsNotNull(listOfDepartments);
    Assert.AreEqual(listOfDepartments.Count<Department>(), 3);
}

Unit Testing in Data Persistence Layer

The Data Persistence Layer contains implementations of the objects responsible for data persistence. Its role is basically to fetch and store data for use in other application layers. We have in this layer the repository implementations, the DbContext class (in the case of the use Entity Framework), the class factory for database (if this pattern is used) and other auxiliary classes. Within the concepts DDD, this layer is linked to the database access technologies through their classes. Either via API .NET (ADO.NET), or via ORM (Entity Framework, Nhibernate, etc). Either through a relational database, or even a NoSQL database.

In this post, a set of class (fake object) will be used to simulate the Entity Framework, it could be another ORM, or any other data access technology. The Entity Framework (EF) is an ORM (Object Relational Mapper) that has a class which is able to provide an access session with the database during runtime, the DbContext class. Through this class, the EF enables you to query, insert, update, and delete data, using application objects (known as entities in the domain layer). The EF maps the entities and relationships that are defined from its domain model to a database. The DbContext class exposes as properties the DbSet objects that represent collection of the specified entities in the context. Each DbSet object represents only a domain entity, in a one-to-one relationship. There is extensive documentation at http://www.asp.net/entity-framework about the Entity Framework.

Why Use A Fake object? Because it's necessary to have an object in memory that meets the Query scenarios (Select) and Non-Query (Insert, Update, Delete), SIMULATING access to a database such as an ORM. An object is needed that returns a record group when queried about them, or that simulates the insertion of a record. A testing framework (Moq, Rhino Mocks, etc.) does not always work with certain versions of an ORM, it is common to version mismatch. For this reason, one fake class structure will be manually created that simulates the responses of a database to meet the needs of testing. These fake classes regardless of any testing framework, provide full control over the action of each method.

The DbSetForTests<TEntity> class derives from the DbSet<TEntity> class and will be used to store data in memory concerning the entity to be tested (TEntity). It also provides methods for the simulation of actions such as Insert, Update and Delete data in a database table, through sync and async methods. In this post, you can see the pros and cons of this approach, in addition to its full implementation.

C#
public class DbSetForTests<TEntity> : DbSet<TEntity>, IQueryable, 
IEnumerable<TEntity>, IDbAsyncEnumerable<TEntity> where TEntity : class
{
    ObservableCollection<TEntity> _data;
    IQueryable _query;

    public DbSetForTests()
    {
        _data = new ObservableCollection<TEntity>();
        _query = _data.AsQueryable();
    }

    public override TEntity Add(TEntity item)
    {
            _data.Add(item);
        return item;
    }

    // This class contains several other methods to update, delete, queries 
    // and async methods.
}

It's also needed to create an implementation of a fake context class using DbSetForTests as a dependency for its DbSet<Entity> properties. Note that in ContextForTests class constructor that each DbSet<T> object is instantiated through a new DbSetForTests<T>.

C#
public interface IContextForTests 
{
    System.Data.Entity.DbSet<Department> CAD_DEPARTMENT { get; set; }
    System.Data.Entity.DbSet<ProductType> CAD_DOCUMENT_TYPE { get; set; }
    System.Data.Entity.DbSet<Product> CAD_DOCUMENT { get; set; }
} 

public class ContextForTests: IContextForTests
{
    public ContextForTests()
    {
        this.CAD_DEPARTMENT = new DbSetForTests<Department>();
        this.CAD_DOCUMENT = new DbSetForTests<ProductType>();
        this.CAD_DOCUMENT_TYPE = new DbSetForTests<Product>();
    }

    public virtual DbSet<Department> CAD_DEPARTMENT { get; set; }

    public virtual DbSet<ProductType> CAD_DOCUMENT_TYPE { get; set; }

    public virtual DbSet<Product> CAD_DOCUMENT { get; set; }
}

Following the sequence of necessary implementations, the ContextForTests class is one of dependencies of DatabaseFactoryForTests. DatabaseFactoryForTests which is also a fake object created exclusively to meet the tests, as well as DbSetForTests and ContextForTests class. Within this class hierarchy, the DatabaseFactoryForTests class has the responsibility to manage the creation of contexts. This fake class structure isn't mandatory, this approach is just a way of dealing with tests for data access. For these examples, the objects were created following the class hierarchy with DbContext (EF), working with the pattern Factory and Generic Repository. Could be any implementation with another structure since that simulates the repository methods in memory.

C#
public interface IDataBaseFactoryForTests : IDataBaseFactory
{
    IContextForTests GetContextForTests();
}

public class DatabaseFactoryForTests : IDataBaseFactoryForTests
{
    private IContextForTests _ContextForTests;

    public DatabaseFactoryForTests(IContextForTests contextForTests)
    {
        _ContextForTests = contextForTests;
    }

    public AppContext GetContext()
    {
        return null;
    }

    public IContextForTests GetContextForTests()
    {
        return _ContextForTests ?? (_ContextForTests = new ContextForTests());
    }
}

After creating the fake structure can be created DepartmentRepositoryTests class, which is a class in order to test the methods of DepartmentRepository class. A repository class as DepartmentRepository performs CRUD operations for the Department domain entity. As already discussed, the DepartmentRepositoryTests class carries out tests using DbSetForTests objects, ContextForTests and DataBaseFactoryForTests. These tests should ensure that the insert, update, delete and query for this repository works properly in any situation.

C#
[TestClass]
public class DepartmentRepositoryTests
{
    IContextForTests _ContextForTests;
           IDataBaseFactoryForTests _DataBaseFactoryForTests;
           IDepartmentRepository _DepartmentRepository;


    [TestInitialize]
    public void Initialize() 
    {
        _ContextForTests = new ContextForTests();
        _DataBaseFactoryForTests = new DatabaseFactoryForTests( _ContextForTests);
    }

    // Example 5:
    [TestMethod]
    public async Task GetAllAsyncMethodRetrievesRecordsInContext()
    {
        _ContextForTests.CAD_DEPARTMENT.Add(new Department { Id = 2, Name = "Department 2" });
        _ContextForTests.CAD_DEPARTMENT.Add(new Department { Id = 3, Name = "Department 3" });
        _ContextForTests.CAD_DEPARTMENT.Add(new Department { Id = 1, Name = "Department 1" });

        _DepartmentRepository = new DepartmentRepository
                ( _DataBaseFactoryForTests, _ContextForTests.CAD_DEPARTMENT);
        var listOfDepartments = await _DepartmentRepository.GetAllAsync();

        Assert.IsNotNull(listOfDepartments);
        Assert.AreEqual(listOfDepartments.Count<Department>(), 3);
        Assert.AreEqual(listOfDepartments[0].Id, 2);
    }

    // Example 6:
    [TestMethod]
    public async Task GetAllAsyncMethodThrowsExceptionWhenDbSetIsNull()
    {
        Exception caughtException = null;
        IList<Department> listOfDepartments = null;

        try
        {
            _DepartmentRepository = new DepartmentRepository(_DataBaseFactoryForTests, null);
            listOfDepartments = await _DepartmentRepository.GetAllAsync();
        }
        catch(Exception err)
        {
              caughtException = err;
        }

        Assert.IsNotNull(caughtException);
        Assert.AreEqual(caughtException.Message, 
"An error occurred while retrieving the list of all entities.");
        Assert.AreEqual(listOfDepartments, null);
    }

    // Example 7:
    [TestMethod]
    public async Task AddAsyncMethodInsertRecordsInEmptyContext()
    {
        _DepartmentRepository = new DepartmentRepository( _DataBaseFactoryForTests, 
_ContextForTests.CAD_DEPARTMENT);
        await _DepartmentRepository.AddAsync(new Department
        { 
            Id = 1,
            Name = "Department 1"
        });
        var listOfDepartments = await _DepartmentRepository.GetAllAsync();

        Assert.IsNotNull(listOfDepartments);
        Assert.AreEqual(listOfDepartments.Count<Department>(), 1);
        Assert.AreEqual(listOfDepartments[0].Id, 1);
        Assert.AreEqual(listOfDepartments[0].Name, "Department 1");
    }

    // Example 8:
    [TestMethod]
    public async Task AddAsyncMethodInsertRecordsInAFilledContext()
    {
        _ContextForTests.CAD_DEPARTMENT.Add(new Department { Id = 2, Name = "Department 2" });
        _ContextForTests.CAD_DEPARTMENT.Add(new Department { Id = 3, Name = "Department 3" });
        _ContextForTests.CAD_DEPARTMENT.Add(new Department { Id = 1, Name = "Department 1" });

        _DepartmentRepository = new DepartmentRepository(_DataBaseFactoryForTests, 
_ContextForTests.CAD_DEPARTMENT);
        await _DepartmentRepository.AddAsync(new Department
        { 
                  Id = 4,
                    Name = "Department 4"
        });
        var listOfDepartments = await _DepartmentRepository.GetAllAsync();

        Assert.IsNotNull(listOfDepartments);
        Assert.AreEqual(listOfDepartments.Count<Department>(), 4);
        Assert.AreEqual(listOfDepartments[3].Id, 4);
        Assert.AreEqual(listOfDepartments[3].Name, "Department 4");
    }

    // Example 9:
    [TestMethod]
    public async Task AddAsyncMethodThrowsExceptionWhenDepartmentIsNull()
    {
        Exception caughtException = null;

        try
        {
            _DepartmentRepository = new DepartmentRepository
                (_DataBaseFactoryForTests, _ContextForTests.CAD_DEPARTMENT);
            await _DepartmentRepository.AddAsync(null);
        }
        catch (Exception err)
        {
            caughtException = err;
        }

        Assert.IsNotNull(caughtException);
        Assert.AreEqual(caughtException.Message, "Not is possible to insert a null Department.");
    }
}

In this moment isn't being performed any operation on a real database. How can we know if these same methods repository will work for a database? These tests don't verify if we can open a connection to the database, or if the connectionstring is correct, or if there is a table in the database to receive and save the records. However the repository methods are able to do what it claims to do, being connected to any context. That is, the Update method should be able to update the entity received by it, a query must get the records that were requested and so on. To run the tests, the data will be stored by DbSetForTests<Department>, a class that derives from DbSet<TEntity>, which is the same class that will be accessed by a repository in any situation. That is, if a repository works by accessing data for DbSetForTests, it should also work to DbSet when connected to a database context.

The Presentation Layer

In the presentation layer, an MVC project ASP.NET will be used. The architectural pattern MVC separates an application into three main components: Model, View and Controller. For a better understanding about the MVC pattern http://www.asp.net/mvc portal is a source of much documentation for learning.

As suggested earlier, the ideal is perform unit testing for any code, since it has some complexity degree. Doesn't matter if the code is written in a View, a Controller or a Model class (ViewModel). For Views is suggested unit testing for the JavaScript code and renderings involving Ajax code. How we know the View code is basically HTML, it doesn't need the unit testing, but there can be scripts.

A good practice regarding front-end code (views) is the exclusion of any logical component of code. Basically, the Views should only have HTML code for displaying information. Although Razor allow scripts written in C#/VB.NET in a view, this isn't a good practice when there is logical code involved. The use of any conditional structure, state variables and any other logic prevents the development of tests for the validation of the data to be displayed. The next example illustrates a situation where it a code snippet for determining the Status of a Billing was added to View.

XML
// Code Snippet of View
<div class="row">
    <h3>Billing Details</h3>
    <br />

    // Display other fields…

    // Code snippet THAT IS BAD SMELL, involving logical part!
    @{
        @Html.Label("Status: ");
        var days = DateTime.Today.Subtract(@Model.InitialDate).Days;
        if (days >= 30){
            <strong>@Html.Label("Ended Month!");</strong>
        }
        <strong>@Html.Label("Month in Progress!");</strong>
    }
</div>

The suggestion to solve this problem is encapsulating in a Model component of the object, in order to receive all logic. Thus, to be sent by the controller, the ViewModel is received by the view with the Status field filled, ready to show the property value.

C#
// View Model
public class BillingViewModel
{
    // Other fields and methods here….

    public DateTime InitialDate { get; set; }

    public string Status
    {
               get
        {
            var days = DateTime.Today.Subtract(InitialDate).Days;
            if (days >= 30){
                return "Ended Month!";
            }
            return "Month in Progress!";
        }
    }
}

The same view could be rewritten as the following code. And the Status property could be tested as any code.

XML
// Code Snippet of a better View
<div class="row">
    <h3>Billing Details</h3>
    <br />

    // Display other fields…

        Status: @Model.Status
</div>

A controller is responsible to receive user interactions of application and displays data through Views. A controller includes Action Methods that can return views. These methods can contain some internal logic with the possibility of routing to either view. There are situations where conditional structures exist that forwards to some exception. Thus, for each routing possibility, it is necessary to generate one unit testing. In the next example, it's possible to return three different views according to the action method, there should be three different tests simulating the three situations.

C#
//Action Method
public ActionResult CreditCardValidation(string CreditCardNumber)
{
    If (string.Empty(CreditCardNumber)){
        return View("Cancel");
    }

    bool isACreditCardNumberValid = CreditCardService.VerifyNumber( CreditCardNumber);

    if (isACreditCardNumberValid){
            return View("Payment");
        }
    return View("CreditCardInvalid");
}

//Tests:

// Example 10:
[TestMethod]
public void CreditCardValidationActionMethodWithEmptyParameter()
{
    var creditCardServiceMock = new Mock<ICreditCardServiceMock>();
    var creditCardController = new CreditCardController(creditCardServiceMock);
    var viewResult = creditCardController.CreditCardValidation(string.Empty) as ViewResult;

    Assert.AreEqual("Cancel", viewResult.ViewName);    
}

// Example 11:
[TestMethod]
public void CreditCardValidationActionMethodWithCreditCardNumberInvalid ()
{
    var creditCardServiceMock = new Mock<ICreditCardServiceMock>();
    creditCardServiceMock
        .Setup(serv => serv.VerifyNumber(It.IsAny<long>()))
        .Returns(false)
    var creditCardController = new CreditCardController(creditCardServiceMock);
    var viewResult = creditCardController.CreditCardValidation("1234 1234 1234 XXXX") as ViewResult;
    
    Assert.AreEqual("CreditCardInvalid", viewResult.ViewName);    
}

// Example 12:
[TestMethod]
public void CreditCardValidationActionMethodWithCreditCardNumberValid ()
{
    var creditCardServiceMock = new Mock<ICreditCardServiceMock>();
    creditCardServiceMock
        .Setup(serv => serv.VerifyNumber(It.IsAny<long>()))
        .Returns(true)
    var creditCardController = new CreditCardController(creditCardServiceMock);
    var viewResult = creditCardController.CreditCardValidation("1234 1234 1234 1234") as ViewResult;
    
    Assert.AreEqual("Payment", viewResult.ViewName);    
}

Some Action Methods of controllers also receive ViewModels objects as parameters. Each parameter must have its fields tested before being passed to the lower layers. It's very important validation of user inputs, even if it's using the DataAnnotations component for this validation. Same using DataAnnotations can there be many types of validation beyond their control.

This link has a post about the testing of session variables through the HttpContext object. This example requires a change in the writing of controllers to make tests easier which leads to reflection about reasons that cause some developers to not test all code. This makes it quite clear that the code is not always ready to be tested, sometimes it's necessary to adapt it for the unit tests.

Points of Interest

When thinking of implementing unit tests, we should keep in mind that not every application is ready to be tested, or easily tested. Therefore, there is a huge difficulty in starting the implementation of unit testing in an application ready. The use of TDD determines that the test to be written before the code, already thinking about a code that meets the tests. Depending on how the application code is implemented is difficult accessing their dependencies, with difficulty to access the classes that are associated with the class under test. Using dependency injection by class constructor or by injection methods allows the insertion of predefined objects in the tests. Thus, instantiating a class, or a method call, tests can pass objects as parameters in a controlled manner. The test examples 10, 11 and 12 of this post show the dependency injection employment in the constructor, when for example is passed as parameter to creditCardServiceMock (mock object) to the creditCardController controller.

By implementing a class, their dependent classes (associated) should be allowed to be instantiated through its constructor or its methods, not instantiated them internally. This makes it much easier to execute tests under the class code.

Although there is a suggestion of unit testing for all application layers, we aren't preachers of test coverage for 100% of the code. In fact, we don't do this in our applications. But when an application is made in parallel with the unit tests, it gives a different approach to a better product. However, when starting the use of tests, there is much initial work that scares those who are starting. We have experienced situations where every time a complex code was executed, tied by the filling of numerous fields and a long runtime, we waited many minutes to check the execution results. Unit tests solve this wait! They improve this complex code! And depending on the application, or part of code, the tests favoring productivity.

This is an introductory post and aimed at those who feel insecure regarding the use of tests. Many examples are basic, in order to explain a simple approach. This is worth more the message than the content itself. Unfortunately, it isn't possible to perform the number of tests that this article suggests, a much larger number of examples and situations could be built. There aren't examples for the application layer, which have unit tests similar to that seen in other layers. Also, there is a lack of various examples of the aforementioned other layers. But we believe that although the issue was addressed briefly, the task to clarify on "What, where, when and who to test your code" was fulfilled.

History

  • 19th February, 2015: Initial version

License

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