views:

133

answers:

6

I'd like to start using unit tests, but I'm having a hard time understanding how I can use them with my current project.

My current project is an application which collects files into a 'Catalog'. A Catalog can then extract information from the files it contains such as thumbnails and other properties. Users can also tag the files with other custom meta data such as "Author" and "Notes". It could easily be compared to a photo album application like Picasa, or Adobe Lightroom.

I've separated the code to create and manipulate a Catalog into a separate DLL which I'd now like to test. However, the majority of my classes are never meant to be instantiated on their own. Instead everything happens through my Catalog class. For example there's no way I can test my File class on its own, as a File is only accessible through a Catalog.

As an alternative to unit tests I think it would make more sense for me to write a test program that run through a series of actions including creating a catalog, re-opening the catalog that was created, and manipulating the contents of the catalog. See the code below.

//NOTE: The real version would have code to log the results and any exceptions thrown

//input data
string testCatalogALocation = "C:\TestCatalogA"
string testCatalogBLocation = "C:\TestCatalogB"
string testFileLocation = "C:\testfile.jpg"
string testFileName = System.IO.Path.GetFileName(testFileLocation);


//Test creating catalogs
Catalog catAtemp = Catalog(testCatalogALocation)
Catalog catBtemp = Catalog(testCatalogBLocation );


//test opening catalogs
Catalog catA = Catalog.OpenCatalog(testCatalogALocation);
Catalog catB = Catalog.OpenCatalog(testCatalogBLocation );


using(FileStream fs = new FileStream(testFileLocation )
{
    //test importing a file
    catA.ImportFile(testFileName,fs);
}

//test retrieving a file
File testFile = catA.GetFile(System.IO.Path.GetFileName(testFileLocation));

//test copying between catalogs
catB.CopyFileTo(testFile);


//Clean Up after test
System.IO.Directory.Delete(testCatalogALocation);
System.IO.Directory.Delete(testCatalogBLocation);

First, am I missing something? Is there some way to unit test a program like this? Second, is there some way to create a procedural type test like the code above but be able to take advantage of the testing tools building into Visual Studio? Will a "Generic Test" in VS2010 allow me to do this?


Update

Thanks for all the responses everyone. Actually my classes do in fact inherit from a series of interfaces. Here's a class diagram for anyone that is interested. Actually I have more interfaces then I have classes. I just left out the interfaces from my example for the sake of simplicity.

Thanks for all the suggestions to use mocking. I'd heard the term in the past, but never really understood what a "mock" was until now. I understand how I could create a mock of my IFile interface, which represents a single file in a catalog. I also understand how I could create a mock version of my ICatalog interface to test how two catalogs interact.

Yet I don't understand how I can test my concrete ICatalog implementations as they strongly related to their back end data sources. Actual the whole purpose of my Catalog classes is to read, write, and manipulate their external data/resources.

+2  A: 

Read about mocking & stubbing

Shady M. Najib
+2  A: 

This is a pure case where Dependency Injection plays a vital role.

As Shady suggest to read about mocking & stubbing. To achive this , you should consider using some Dependency Injectors like ( Unity in .net).

Also read about Dependency Injection Here

http://martinfowler.com/articles/injection.html

saurabh
Shady M. Najib
+1  A: 

the majority of my classes are never meant to be instantiated on their own

This is where the D - the Design D - comes into TDD. It's bad design to have classes that are tightly coupled. That badness manifests itself immediately when you try to unit test such a class - and if you start with unit tests, you'll never find yourself in this situation. Writing testable code compels us to better design.

I'm sorry; this isn't an answer to your question, but I see others have already mentioned mocking and DI, and those answers are fine. But you put the TDD tag on this question, and this is the TDD answer to your question: don't put yourself in the situation of tightly coupled classes.

Carl Manaster
Good point. Sometimes the best answers are those which you were not expecting.
Eric Anastas
+4  A: 

You ought to read about SOLID code principles. In particular the 'D' on SOLID stands for the Dependency Injection/Inversion Principle, this is where the class you're trying to test doesn't depend on other concrete classes and external implementations, but instead depends on interfaces and abstractions. You rely on an IoC (Inversion of Control) Container (such as Unity, Ninject, or Castle Windsor) to dynamically inject the concrete dependency at runtime, but during Unit Testing you inject a mock/stub instead.

For instance consider following class:

public class ComplexAlgorithm
{
    protected DatabaseAccessor _data;

    public ComplexAlgorithm(DatabaseAccessor dataAccessor)
    {
        _data = dataAccessor;
    }

    public int RunAlgorithm()
    {
        // RunAlgorithm needs to call methods from DatabaseAccessor
    }
}

RunAlgorithm() method needs to hit the database (via DatabaseAccessor) making it difficult to test. So instead we change DatabaseAccessor into an interface.

public class ComplexAlgorithm
{
    protected IDatabaseAccessor _data;

    public ComplexAlgorithm(IDatabaseAccessor dataAccessor)
    {
        _data = dataAccessor;
    }

    // rest of class (snip)
}

Now ComplexAlgorithm depends on an interface IDatabaseAccessor which can easily be mocked for when we need to Unit test ComplexAlgorithm in isolation. For instance:

public class MyFakeDataAccessor : IDatabaseAccessor
{
    public IList<Thing> GetThings()
    {
        // Return a fake/pretend list of things for testing
        return new List<Thing>()
        {
            new Thing("Thing 1"),
            new Thing("Thing 2"),
            new Thing("Thing 3"),
            new Thing("Thing 4")
        };
    }

    // Other methods (snip)
}

[Test]
public void Should_Return_8_With_Four_Things_In_Database()
{
    // Arrange
    IDatabaseAccessor fakeData = new MyFakeDataAccessor();
    ComplexAlgorithm algorithm = new ComplexAlgorithm(fakeData);
    int expectedValue = 8;

    // Act
    int actualValue = algorithm.RunAlgorithm();

    // Assert
    Assert.AreEqual(expectedValue, actualValue);
}

We're essentially 'decoupling' the two classes from each other. Decoupling is another important software engineering principle for writing more maintainable and robust code.

This is really the tip of the tip of the iceberg as far as Dependency Injection, SOLID and Decoupling go, but it's what you need in order to effectively Unit test your code.

Sunday Ironfoot
Thanks this this really helped me understand mocking, which I never really got before. However, in my case the real meat of my program is the, data accessing, Catalog class. From your example what if your project was the DatabaseAccessor. How could you test it?
Eric Anastas
+1  A: 

Here is a simple algorithm that can help get you started. There are other techniques to decouple code, but this can often get you pretty far, particularly if your code is not too large and deeply entrenched.

  1. Identify the locations where you depend on external data/resources and determine whether you have classes that isolate each dependency.

  2. If necessary, refactor to achieve the necessary insulation. This is the most challenging part to do safely, so focus on the lowest-risk changes first.

  3. Extract interfaces for the classes that isolate external data.

  4. When you construct your classes, pass in the external dependencies as interfaces rather than having the class instantiate them itself.

  5. Create test implementations of your interfaces that don't depend on the external resources. This is also where you can add 'sensing' code for your tests to make sure the appropriate calls are being used. Mocking frameworks can be very helpful here, but it can be a good exercise to create the stub classes manually for a simple project, as it gives you a sense of what your test classes are doing. Manual stub classes typically set public properties to indicate when/how methods are called and have public properties to indicate how particular calls should behave.

  6. Write tests that call methods on your classes, using the stubbed dependencies to sense whether the class is doing the right things in different cases. An easy way to start, if you already have functional code written, is to map out the different pathways and write tests that cover the different cases, asserting the behavior that currently occurs. These are known as characterization tests and they can give you the confidence to start refactoring your code, since now you know you're at least not changing the behavior you've already established.

Best of luck. Writing good unit tests requires a change of perspective, which will develop naturally as you work to identify dependencies and create the necessary isolation for testing. At first, the code will feel uglier, with additional layers of indirection that were previously unnecessarily, but as you learn various isolation techniques and refactor (which you can now do more easily, with tests to support it), you may find that things actually become cleaner and easier to understand.

Dan Bryant
"1) Identify the locations where you depend on external data/resources and determine whether you have classes that isolate each dependency." . . .. . .See the problem I have is the purpose of my program is to read, write, and manipulate external data. So yes I have a class that isolates this functionality (ICatalog/Catalog), but this interface/class is also the real meat of my program which I'd like to test.
Eric Anastas
@Eric, if it's the meat of your program, then it's not really completely isolating the dependency. There's too much logic intertwined with the isolation. Consider all of the calls you have to the external data and see if you could extract them into an adapter object that simply forwards the calls to the external dependency. Once you have this, you now have a class that provides a clear isolation, with no additional responsibilities. At this point, you can proceed with the other steps, extracting an interface and using it for testing.
Dan Bryant
My Catalog class, which implements ICatalog, is actually an abstract class which implements ICatalog by referencing additional abstract methods that simply read and write the data to/from the Catalog (WriteFile(), ReadFile(), CreateDBRecord(), ReadDBRecord() etc. These abstract methods are then defined in concrete implementations of Catalog. So what you're saying is instead of having these methods be abstract encapsulate all of them in an interface such as ICatalogDataAdapter. So different implementations of Catalog would really just be different implementations ICatalogDataAdapters. Right?
Eric Anastas
@Eric, I'm not sure if I fully understand what those methods are doing, but I think you're on the right track. You might need to create different adapters for the different concrete implementations of Catalog, but it really depends on the types of operations you're performing. I would make a list of all the places in your code that purely access external data (such as reading from a stream or executing a SQL query) and see if you can identify natural boundaries where you might want to fake particular data for your tests.
Dan Bryant
A: 

What you have now is Legacy Code. That is: code that has been implemented without tests. For your initial tests I would definitely test through the Catalog class until you can break all those dependencies. So your first set of tests will be integration/acceptance tests.

If you don't expect for any behavior to change, then leave it at that, but if you do make a change, I suggest that you TDD the change and build up unit tests with the changes.

Gutzofter