Click here to Skip to main content
15,881,797 members
Articles / Programming Languages / C#
Article

Testable Applications

Rate me:
Please Sign up or sign in to vote.
3.30/5 (9 votes)
20 Apr 200712 min read 45.2K   198   35   6
Make your application testable.

Testable Application Image

Introduction:

Many of us hear today about unit test, and mock objects, other may touched unit test first

time with NUnit tool

http://www.nunit.org/

Or through visual studio 2005 Team Suite, There are many articles about how to create unit

test, or how to test your applications And how to use different patterns for test.


This is not the target of this article, this article focus in how to create testable application

Or how to refactor your normal application code, to be testable code.

Acknowledgment:

I want to thank first person, introduce the unit test and mock objects to me, through a quick

training session, he is Mark Focas Senior Analyst Programmer, Center for Learning

and Innovation

My own experience:

I did not realized the important of unit test, until I worked in a big application it contains

more than 15 .Net projects this application did not rely on dataset at all, so all objects are

custom objects and collections, so the total number of classes about few thousands, to

control this code without unit test is quite impossible.


Each time I finished module I want to know what is the impact in the other modules in the

application. at the same time, it saves time, in web application development, to write the

code and run the web application each time you finished the code, it will be killing process.


So after creating thousands of unit tests, and running all of them after finishing each

module I can recommend to work with unit test even if your application looks simple.

Quick definitions:

Unit Test:

Unit test is automatic test, test a unit or piece of code may be an object, method in object,

or property, or any other unit through a Framework is called Nunit

this Framework give you all the tools that you need for comparison, assertion, expected

Exceptions ,.. that you need to test your applications.

Mock object :

Is an Object that emulate (act as) other real object dependency, so we can isolate the

objects from their dependencies as possible to focus in each object itself.

Integration test:

The tests that use 1 or more objects and dependency, to see the effect of each object

works with its dependencies

Bug fixing:

Bug fixing is a process of changing a piece of code and running a unit test to test this code,

then running an integration test to test the effect of this change of the other code.

Using the code:

  1. First you should download Nunit open this page

    http://www.nunit.org/index.php?p=download


    then choose win .net 2.0 NUnit-2.4.0-r2-net-2.0.msi

  2. Then unzip the article code, and open the article solution in Visual studio 2005 and

    make UnitTest Project is the Start up project.

  3. Choose UnitTest project properties and be sure that the Start external program: field

    And Working Directory are pointing to the right location.

    figure 1, the unit test application configuration
    Image 2
  4. Run the application, the program will launch the Nunit , then Click Run You should see

    this screen

    figure 2, the unit test result

Image 3

The Project's Idea:

The project simply is an example of simple billing system, The Client will fill the invoice fields

data then will Issue the Invoice With this process the Invoice Database will be effected and

the Customer database will be effected as well,to Debt the customer with the total value of

the invoice.

The Payoff process will simply pay off the invoice, and will update the Customer account with

the transaction

figure 3, 2 versions of Code Testable, and non testable
Image 4

for the demo we created 2 versions of the Project Objects

  • Non Testable Objects

    All objects that under the Nontestable namespace

  • Testable Objects.

    All objects that under the Testable namespace All objects that under the Testable

    namespace

Characteristics of Non Testable Applications:

Every application can be testable, as manual test or minor automatic test,

the QA department is still testing the application through running the application and creating

different scenarios,in different Platform(s) but we are focus in the Automatic unit test, that

made by programmer
as a part of his job .

To create unit tests for Non testable application is not easy at all, because simply you always

will test the outer shell of integration test, and in most case you

could not know where is the bug, unless you step in the code line by line to know where the

code was broken and we can summarize the Non testable application

1) Is not separated in well designed layers:

we consider that is the same condition for well designed OOP, if your application is not well

separated in different layers it means you could not design objects had specific

responsibilities , and encapsulate the internal implementation.

2) The code is not reusable:

That is the first step of refactoring the code, we gather the common code in methods,

then we may encapsulate the code that has identity to objects, then abstract the objects

to more abstracted object, until we find that there are no redundancy in the code at all,

the result will be very clear , reusable and wonderful maintainable application

3) The Code hides internal dependency that we could not isolate:

This point is first point that differentiate the testable application and good OOP application,

we know that the concept of OOP is built in Encapsulation.

So we need both the code be encapsulated and hide the internal implementation and

be open for changing the dependency in runtime "And that is the Challenge"

4) The objects has not any contracts :

If the object has not any contract (interface or abstracted class that inherit) it means that

client object .will consume the concrete class not the abstracted or the interface, And finally

we will end up with very static code, unchangeable So it will be very hard to test.

5) The objects methods and properties are not virtual:

This requirement Is not mandatory but it will be helpful if you think

to replace any object with another one in runtime for testing purpose It means that in run

time, the client class still see the old implementation not the new implementation that

supplied with the new version (new subclass or new inherited class) ,

and we will end up with non testable application because we could not change the behavior

of the objects in runtime. if you already worked with automatic mock objects, specially in

.Net 1 this concept will be more clear.

Apply these simple concepts on the sample code:

figure 4, class diagram of non testable code
Image 5

This design is a good sample for Non testable Application for these Reasons

  1. The Implementation of the business objects and data objects exists in the same place,

    it is very hard to separate the objects in layers so we have at least 2 layers mixed in

    one layer.

  2. The code is not reusable , we found the method ExecuteStoredProcedure is common in

    Invoice object and Customer object, however it is implemented twice, so we can see

    clearly that we need a new object like a DalcBase to encapsulate all these common

    methods in abstracted class, then we will inherit for each object his own Dalc.

  3. The Code hide non reachable implementation, ExecuteStoredProcedure is private and it

    lives inside the objects and you could not access this method to change its behavior for

    test.

  4. All objects have not a contract neither interfaces or abstracted class it means , that all

    Client classes use the concrete class directly and that is obvious in invoice class use

    the Customer Class not CustomerBsase or ICustomer
figure 5, Sample of non testable code.
using System;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlClient;
using System.Data;

namespace TestableApplication.NonTestable
{
    public class Invoice
    {
        private int _invoiceNumber;
….

Customer _customer;
        public Customer Customer
        {
            get { return _customer; }
        
        }
}
}

In this code we can see that the Invoice object use the Customer object directly

not interface or abstracted class, so we could not change the Customer dependency in

runtime , and we could not create a mock object that inherit Customer because all private

methods and fields are inaccessible , so now we end up with fixed dependency non

Changeable.

Effect of the Non testable Application on the Unit test:

figure 6, Sample of non testable result.
testable_applications/NonTestableResult.GIF

In this figure 6 we see that the same error that happened in the Customer test in Debt

method, is the same in all IssueInvoice Unit tests The Error is "InvalidOperationException

Customer Debt method, This Error is done in purpose as Example of Customer Dependency

Error" Let us see the code

public class Customer{

public void Debt(decimal value, string description, int transactionID,DateTime transactionDate)
        {
            throw new InvalidOperationException("Customer Debt method, This Error did in
                purpose as Example of Customer Dependency Error");             
            Console.WriteLine("--- Debt Method was invoked from object {0} ", this);
            Console.WriteLine("press enter to continue ....");
            Console.ReadLine();

            List<sqlparameter __designer:dtid="844424930132191"> parameters = new List<sqlparameter>();
            SqlParameter param = new SqlParameter("@VALUE", SqlDbType.Money);
…………..
}
</sqlparameter></sqlparameter>

This Exception was thrown inside Customer Debit method, however it was happened in the full

cycle of Issue invoice method test. Now let us see the Issue invoice Code
public Invoice IssueInvoice(string description, Customer customer, List<invoiceitem __designer:dtid="844424930132194"> items)
        {
            Console.WriteLine("--- IssueInvoice Method was invoked from object {0} ", this);
            Console.WriteLine("press enter to continue ....");
            Console.ReadLine();

            List<sqlparameter> parameters = new List<sqlparameter>();
            SqlParameter param = new SqlParameter("@DESCRIPTION", SqlDbType.NVarChar);
            param.Value = description;
            parameters.Add(param);

            param = new SqlParameter("@CUS_ID", SqlDbType.Int);
            param.Value = customer.ID; ;
            parameters.Add(param);

            DateTime issueDate = DateTime.Now;
            param = new SqlParameter("@ISSUE_DATE", SqlDbType.DateTime);
            param.Value = issueDate;
            parameters.Add(param);

            param = new SqlParameter("@DUE_DATE", SqlDbType.DateTime);
            param.Value = issueDate.AddMonths(1); ;
            parameters.Add(param);

            object idObj = ExecuteStoredScalerProcedure("stp_add_invoice_header", parameters);
            // for test only
            idObj = DateTime.Now.Millisecond; 
            int invoiceNumber = int.Parse(idObj.ToString());

            parameters.Clear();
            int seq = 0;
            foreach (InvoiceItem item in items)
            {

                param = new SqlParameter("@INV_ID", SqlDbType.Int);
                param.Value = invoiceNumber;
                parameters.Add(param);

                param = new SqlParameter("@QTY", SqlDbType.Int);
                param.Value = item.Quantity;
                parameters.Add(param);

                param = new SqlParameter("@SEQ_NUM", SqlDbType.Int);
                param.Value = ++seq;
                parameters.Add(param);
                ExecuteStoredScalerProcedure("stp_add_invoice_line", parameters);

            }
            Invoice newInvoice=new Invoice(invoiceNumber, description, customer, issueDate,Status.Issued, items);
// always cause error            
customer.Debt(newInvoice.TotalAmount, newInvoice.Description, newInvoice.InvoiceNumber, newInvoice.IssueDate);
            return newInvoice;
        }
</sqlparameter></sqlparameter></invoiceitem> 
The Issueinvoice, has code to create new Invoice instance , and to update the invoice

database and to notify the customer with the invoice value, that he should pay.

So if any thing wrong was happened in invoice database or customer object,

the IssueInvoice method will fail, which is not good;


The good design that will test the database updates separately in unit test, and the

customer Debt method in different unit test and finally the issueInvoice method separately

after all the code pass the unit test we will run the integration test for all of them together .



And now, let us talk about, the good OOP , testable design

Characteristics of Testable Applications:

  1. Is separated in well designed layers , as above it is one of the OOP design condition for

    good encapsulation and maintainable application.

  2. The code should be reusable, all methods should be in appropriated objects, and the

    common members should be in abstracted layer.

  3. The Code should expose internal dependencies as interfaces.

    All dependencies should be gathered in specific objects, then we should extract

    interface(s), and let these dependency interfaces appear as properties in object (or

    may be passed as parameter in special constructor as we will explain soon) ,

    With this simple Idea we can inject the object through the properties (or constructors)

    with this dependency This idea is perfect but unfortunately it violates the concept of

    OOP, and it will open the hidden implementation to the client, which is not good, and

    the objects will not be secure at all.
  4. Objects should always implement interfaces. That is the best practice,

    Specially if you want to use the external mock objects,

    If you like to use abstracted class always as contract (to write your own default

    Implementation), you may need to wrap all abstracted classes with the interface and,

    let all the client objects use interfaces not the concrete or even the abstracted

    classes
  5. The objects methods and properties may be virtual, It is not a mandatory it is just

    recommended in special situations, it will be useful In case of creating a mock object

    from the class directly, to create a quick custom mock object

    But If all classes already implement interfaces, there is no need to make all public

    members virtual, that is already side effect as delay

    in performance for virtual members rather than non virtual members

Steps of refactoring the old code to make it testable code:

  1. Isolate all objects in different layers:

    In our example all code under Testable namespace is separated in 2 layers Business

    component layer and Dalc layer

    figure 7, Sample of testable code, separated in layers.

    Image 7
  2. Let all objects implement interfaces, and let the client classes use these interfaces

    figure 8, Sample of testable code, each object has an interface.

    Image 8

    In this diagram, was extracted interfaces from classes and we implemented all these

    classes
    public partial class Invoice : ICustomer Customer
            {
                get { return _customer; }
    
            }
    
            private IInvoiceDalc _invoiceDalc;
    
    //expose dependancy as interface        
    public ^__strong __designer:dtid="844424930132291">IInvoiceDalc InvoiceDalc
            {
                get {
                    if (_invoiceDalc == null) _invoiceDalc = new InvoiceDalc(this);
    
                    return _invoiceDalc; }
                    
                set {
                    this._invoiceDalc = value;    
                    }   
            }
            
            private int _invoiceNumber;
            public int InvoiceNumber
            {
                get { return _invoiceNumber; }
                set { _invoiceNumber = value; }
            }
    We can see hear how we exposed the Customer as Interface and we did that with the

    Invoice Dalc as well

  3. All dependencies should be gathered in specific objects

    figure 9, Sample of testable code, Dalc objects as sample of ,encapsulation all dependencies in objects.



    Image 9

    In this diagram, we encapsulated all database, methods to Dalc objects, then we made

    all these classes implemented interfaces and we used these interfaces to expose the

    Dalc objects in all objects that use data base through Dalc.
  4. Using Custom Mock objects:

    To create a Custom Mock object, you should implement the same interface of the

    mocked object,and in your unit test, replace the real object with the mock object,

    You can implement the mock object with different ways, may be you will leave all

    methods as empty methods, or Logging all call in log file, or generate and return data

    for specific methods and parameters, or save data in hash table,

    There is a lot of use of mock objects, but in our example we made a simple log to

    Console just to indicate that the methods was invoked,

    We did that just to isolate the dependency (like invoice Dalc, and customer

    Dependency for Invoice Object) to focus on the main object (Invoice object in our

    sample)

    figure 10, Sample of testable code, each object implements interface.

    Image 10


    figure 11, Sample of testable code, Mock Objects should implement the same object interface.


    Image 11

Test testable code:

the main technique fro unit test is, replacing dependencies, in run time.

Change the Dependency in Runtime:

The most famous techniques, are:
  1. Dependency Injection.



    To do that with the new design is not hard at all, but the problem is,

    if we add Set property to our code, our classes will not be secure, and it will violate the

    OOP design.

    In this case you may use #if DEBUG directive for Dependency Injection

    So what you should do, Expose the dependency as interface, and create a Set

    accessors (in Debug mode only ) to change it in runtime
    public partial class Invoice : IInvoice
        {
            private ICustomer _customer;
            public ICustomer Customer
            {
                get { return _customer; }
    
            }
    
            private IInvoiceDalc _invoiceDalc;
            public IInvoiceDalc InvoiceDalc
            {
                get
                {
                    if (_invoiceDalc == null) _invoiceDalc = new InvoiceDalc(this);
    
                    return _invoiceDalc;
                }
    #if DEBUG
                set
                {
                    this._invoiceDalc = value;
                }
    #endif
            }


    the code above is a sample of dependency injection in debug mode

    this code is a sample of using dependency injection in unit test
    Invoice invoice = new Invoice().GetInvoice(12);
                IInvoiceDalc invoiceDalc = new InvoiceDalcMock();
    
                            int count1 = invoice.Items.Count;
                invoice.Items.Add(new InvoiceItem(45, "ss", 55, 23.4m));
    //will not generate error^__strong __designer:dtid="844424930132370">
        
    
        invoice.Update();
                int count2 = invoice.Items.Count;
                Assert.IsTrue(count2 == count1 + 1, "Item Count")
  2. Constructor Injection


    it is very easy to create constructor (or any other method) injection in .Net 2 through

    using partial classes
    namespace TestableApplication.Testable
    {
        public partial class Invoice : IInvoice // only has a constructor injection
        {
    
    // this code will work only in debug mode
    #if DEBUG 
            public Invoice(int invoiceNumber, string description, ICustomer customer, InvoiceDalc invoiceDalc/*Dependency*/)
                : this(invoiceNumber, description, customer)
            {
                this._invoiceDalc = invoiceDalc;
            }
    
            public Invoice(int invoiceNumber, string description, ICustomer customer, DateTime issueDate, Status status, List<invoiceitem __designer:dtid="844424930132376"> items, InvoiceDalc invoiceDalc)
                : this(invoiceNumber, description, customer, issueDate, status, items)
            {
                this._invoiceDalc = invoiceDalc;
    
            }
    
            public Invoice(InvoiceDalc invoiceDalc)
            {
    
            }
    #endif
        }
    }
    </invoiceitem>


    we made the constructor injection (or any other method injection)

    <unli __designer:dtid="844424930132379">
  3. in #if DEBUG


    to be sure that this code is working only in debug mode, so your class in release mode

    will be secure, and encapsulate all deatils and dependencies

  4. partial class


    to put all extra code in separate file, and gather all these files in special folder and you

    can exclude it before release. Now we can test this object using this unit test code

    [Test]
            public void InvoiceTestUpdate_UsingDalcMock2()
            {
                IInvoiceDalc invoiceDalcMock = new InvoiceDalcMock();
                Invoice invoice = new Invoice(123, "description test", customerMock, invoiceDalcMock);
                
                int count1 = invoice.Items.Count;
                invoice.Items.Add(new InvoiceItem(45, "ss", 55, 23.4m));
                invoice.Update();
                int count2 = invoice.Items.Count;
                Assert.IsTrue(count2 == count1 + 1, "Item Count");
    
            }


    Let us see the effect of the new design and with the small design guidelines for the

    test result

    figure 12 , The Testable application, will fail only in faulty code, not the container code

    testable_applications/testResultForGoodTest.GIF

    That is a graet result

    For the first glance you can see the Update method of invoice and

    Debt method of customer Failed and all the other code is working fine

    After while you can see, that the UpdateMethid using DalcMock is working fine,

    it means the problem will be in Update Method of Dalc Method not of Invoice Update

    Method and if you write the InvoiceDalc unit test your code will fail in Update method

    Conclusion

    Before you create a unit test , your code should be testable And to make your code

    testable you have to follow the guide lines of testable application.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior) NSW Curriculum & Learning Innovation Centre
Australia Australia
I am a senior developer self taught,
the main study is electronics and communication engineering

I am working as senior programmer in center for learning and innovation
www.cli.nsw.edu.au

I develop Software since 1995 using Delphi with Microsoft SQL Server

before 2000, I didn’t like Microsoft tools and did many projects using all database tools and other programming tools specially Borland C++ Builder, Delphi, Power Builder
And I loved Oracle database for its stability until I was certified as Master in Database Administration from Oracle University.

I tried to work in web programming but I felt that Java is hard and slow in programming, specially I love productivity.

I began worked with .Net since 2001 , and at the same time Microsoft SQL Server 7 was very stable so I switched all my way to Microsoft Tech.
I really respect .Net Platform especially in web applications

I love database Applications too much
And built library with PowerBuilder it was very useful for me and other developers

I have a wide experience due to my work in different companies
But the best experience I like in wireless applications, and web applications.
The best Application I did in my life is Novartis Marketing System 1999 it takes 8 months developing with PowerBuilder and Borland C++, SQL Server
Performance was the key challenge in this Application.
The other 2 applications that I loved Multilingual Project in Scada company in Italy 2000 and SDP Mobile media content platform server for ChinaUnicom 2004
I hope that you enjoy any Article I post.
God bless you.

Comments and Discussions

 
GeneralO/R Mapper Pin
ianvink@gmail.com24-Jul-07 1:29
ianvink@gmail.com24-Jul-07 1:29 
If you have a project with a massive number of classes for your DAL, use an OR mapper like www.llblgen.com . This will generate a lot of your plumbing code reducing what you have to test.
GeneralRe: O/R Mapper Pin
Refky Wahib26-Jul-07 12:22
Refky Wahib26-Jul-07 12:22 
GeneralInteresting article, but Pin
Bassam Saoud22-Apr-07 3:54
Bassam Saoud22-Apr-07 3:54 
GeneralRe: Interesting article, but Pin
Refky Wahib22-Apr-07 11:19
Refky Wahib22-Apr-07 11:19 
GeneralRe: Interesting article, but Pin
Refky Wahib24-Apr-07 17:34
Refky Wahib24-Apr-07 17:34 
GeneralRe: Interesting article, but Pin
Bassam Saoud24-Apr-07 19:45
Bassam Saoud24-Apr-07 19:45 

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.