Welcome to the Class Tester

This project started life as a blog post and some source code (read the original post) but due to public demand (at least 2 comments!) it has now gone open source. The project has undergone a fairly significant refactoring since then with a number of new features and improvements:

Click the links above for more information on each of the pieces.

Project Description

The idea is pretty simple - a way to easily test your domain classes. You know, the plain old data shapes that look something like this:

public class Person
{
    private string _name;

    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }

    private DateTime _dateOfBirth;

    public DateTime DateOfBirth
    {
        get { return _dateOfBirth; }
        set { _dateOfBirth = value; }
    }

    // etc..
}


Stuff that nobody ever tests directly because it would be so time consuming. I found this very annoying because of the loss of code coverage in my test suite and then disaster struck. I made a change to such code that accidentally broke one of the properties (two different public getters returned the same private string).

Something had to be done and the Automatic Class Tester project was born!

What it can do:

  • Automatically test properties, spotting any miswired getters or setters
  • Increase code coverage, reaching parts manual tests don't even try to
  • Supports ignoring specified properties if you have any funky logic in there that needs a manual test
  • Tests for PropertyChanged events if your class implements INotifyPropertyChanged
  • Tests constructors and that they map parameters to properties
  • An exception is thrown when a problem is found which should cause your tests to fail.

How it works

The PropertyTester simply uses reflection to discover the subject's properties. Random values are then injected via the property setters and the getter is invoked and the ClassTester verifies that we get the same value back. Here we test our Person class above:

[TestMethod]
public void TestPerson()
{
    ClassTester tester = new ClassTester(new Person());
    tester.TestProperties();
}


Some properties just can't be tested in this way, either because they perform some funky logic that is dependant on other state in the class or because they don't return the same value that was set. Like this nonsensical example below that we'll add to our person class:

private int _nonsensicalProperty;

public DateTime NonsensicalProperty
{
    get { return 0; }
    set { _nonsensicalProperty = value; }
}


No problem, we can still use the tester by specifying that this property should be ignored.

[TestMethod]
public void TestPerson()
{
    PropertyTester tester = new PropertyTester(new Person());
    tester.IgnoredProperties.Add("NonsensicalProperty");
    tester.TestProperties();
}

INotifyPropertyChanged support

This is perhaps the most compelling use of the tester given how easy it is to mess up firing the INotifyPropertyChanged due to the lack of compile time support (it's based on strings).

public class Person : INotifyPropertyChanged
{
    private string _name;

    public string Name
    {
        get { return _name; }
        set 
        { 
            _name = value; 
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("Name"));
        }
    }

    // etc..


Again, if a specific property doesn't support INotifyPropertyChanged for some reason, then just add it to the ignored list and test it manually.

Testing Constructors


The QualityTools library can also help you test your constructors thanks to the ConstructorTester. It's a very common practice to create a custom constructor to help inject property values at construction time. Like this:

public class Person
{
    public Person(string name, DateTime dateOfBirth)
    {
        _name = name;    
    }
    
    // etc


[Test]
public void TestPersonConstructors()
{
    ConstructorTester tester = new ConstructorTester(typeof(Person));
    tester.TestConstructors(true);
}


This static method will test all constructors available on the object by creating random values for each parameter (as best it can). Again, if there are some constructors that you need to skip over because they have some funky logic you can add them to a blacklist. Maybe you have to blacklist a particular constructor because it takes a parameter that the ConstructorTester can't create, like an Interface.

[Test]
public void TestPersonConstructors()
{
    ConstructorTester tester = new ConstructorTester(typeof(Person));
    // we specify the signature of the constructor to be ignored
    tester.IgnoredConstructors.Add(typeof(string), typeof(ISomeInterface), typeof(object));
    tester.TestConstructors(true);
}

Testing whole assemblies


To help you test large numbers of classes at once there is even an assembly tester that will test all the properties and constructors, of all the classes.

[Test]
public void AssemblyTester_PassWithExclusions()
{
    AssemblyTester tester = new AssemblyTester("ExampleAssembly");
    // exclude a namespace from the test scope but not child namespaces
    tester.Exclusions.AddNamespace("ExampleAssembly.SomeNamespace", false);
    // first parameters says test properties, second parameter says test constructors
    tester.TestAssembly(true, true);
}


In this example we're going to test all the types within the ExampleAssembly (obviously, it uses the PropertyTester and ConstructorTester under the covers). We also specifically exclude the ExampleAssembly.SomeNamespace so any types belonging to that namespace won't be tested.

Remember kids! This is just a helper and isn't intended to remove the need to write unit tests. It's only there to cover the basic stuff that people don't normally test. If you exclude a namespace, type, property or constructor then you should consider adding a manual test.

Last edited Jan 4, 2008 at 1:06 PM by molmorg, version 22