Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit Testing - not testable code converted to testable code

I have read so many places is that if your code is not test-able that mean code is not well written. So that makes me start writing a code that is test-able and to start using some unit testing framework.

With this though I start looking for some example with piece of code that is not testable and gradually converted to a testable code. I find tons of examples on unit testing but if someone can provide an example like above it probably can jump start things for me.

TIA

like image 983
imak Avatar asked Dec 23 '10 16:12

imak


People also ask

What is non testable code?

The term non-testable is used since, from the point of view of correctness testing, if one cannot decide whether or not the output is correct or must expend some extraordinary amount of time to do so, there is nothing to be gained by performing the test.

What makes code difficult for unit testing?

Tightly coupled code is extremely hard to unit test (and probably shouldn't be unit tested - it should be re-factored first), because by definition unit tests can only test a specific unit of something. All calls to databases or other components of the system should be avoided from Unit Tests because they violate this.

Should unit tests be written before code?

According to that line of reasoning, writing unit tests before writing code is a recommended way to do proper software engineering. Technically speaking, this "measure twice, cut once" approach is called a "test-first" approach.


2 Answers

Here are two great books that will help you get started:

  1. The Art of Unit Testing
  2. Working Effectively with Legacy Code

Good luck.

like image 179
Steven Avatar answered Sep 17 '22 19:09

Steven


Put a bunch of code in a button click event and try to unit test it. It's not impossible, but will either be non-trivial or require some copy-paste finagling to get it done.

protected void buttonClick(object sender, EventArgs e)
{
    string currUser =
        User.Identity.Name.ToString().Trim()
            .Substring(User.Identity.Name.ToString().Trim()
            .IndexOf("\\") + 1);

    Inventory.Employee.DB objEmpDB = new Inventory.Employee.DB();
    Inventory.Employee.Details objEmpDetails = 
        new Inventory.Employee.Details();

    objEmpDetails = objEmpDB.Get(currUser);

    Welcome.Text = 
        "Current User: " + objEmpDetails.Employee_Full_Name;

    var objUserDetails = new Inventory.User.Details();
    Inventory.User.DB objUserDB = new Inventory.User.DB();

    if (objUserDB.UserAuthenticates(currUser))
    {
        objUserDetails = objUserDB.Get(currUser);
        currUserToken = objUserDetails.User_Token.Value;

        userID.Text = currUser;

        if (objUserDetails.Active_User_Name != objUserDetails.User_Name)
        {
            lShadow.Text = "Showin: " + objUserDetails.Active_User_Name;
            lServer.Text = "(" +
            objUserDB.UserPermissionName(objUserDetails.Active_Logon_Name)
                + ") - " + System.Environment.MachineName;
            lShadow.ToolTip = Inventory.Properties.Settings.Default
                .connectionString.Substring(0, Inventory.Properties
                .Settings.Default.connectionString.IndexOf(';'));
            divShadow.Visible = true;
        }
        else
            divShadow.Visible = false;

        lWelcome.Text = "Current User: " + objUserDetails.User_Name;
    }
}

Not only is this hard because of the difficulty of emulating a user button click, but look how much is going on in that button click. If your unit test fails, there's about 100 freakin things that could have gone wrong. DRY, single concern, and other design principles lead to code that's easy to test and easy to fix. After all, what good is a unit test if you are testing brigades rather than units :)

UPDATE: (How to fix the above code)
I'm not going to pretend that the code above is an easy fix. That's a "small" sample from a code base I've worked on in the past. I wanted to show how bad things can get in real life.

There's two major problems with the code.

  1. Its hard to test button click events.
  2. There's too much going on in one method.

Its easy to fix the Event driven/reproducing a button click event problem. You can wrap all that code into another method:

protected void buttonClick(object sender, EventArgs e)
{
   EasyToCallMethod();
}

public void EasyToCallMethod()
{
    string currUser =
        User.Identity.Name.ToString().Trim()
        .Substring(User.Identity.Name.ToString().Trim().IndexOf("\\") + 1);
    //...rest of code
}

Now its easy to call from a unit test. But, that's a little silly because it really doesn't solve the second problem.

Easy Fix
So there's a good 15-20 tests that we can make out of this one method call. Just make a test for each line that has a specific purpose (like where method calls are made) and you should have good unit tests that are small enough to tell where something broke and good code coverage.
Advanced stuff
Much more work can be done. We can implement n-tier MVC or MVVM . At some point, you have to ask yourself if you are over-engineering. Unit tests should make your code more maintainable, but don't over-abstract yourself into nothingness. This is where your own style and experience come into play. When you feel like you've got the basics you should come back to SO with new questions or pickup a good book.

like image 28
P.Brian.Mackey Avatar answered Sep 17 '22 19:09

P.Brian.Mackey