Unit Tester Product Tour
Introduction
To use the Unit Tester you will need to create a 'test harness' project, this will
typically be a Windows Forms application but it does not have to be. In the test
harness create a reference to the component that you are going to test and then
create a reference to the Unit Tester component. You can then code and run your
unit tests from within the test harness using the Unit Tester component.
In this tour of the Blayd Software Unit Tester component we are going to discuss
the main classes in the Unit Tester and outline their functionality and use. We
won't be able to cover all of the classes in the component or all of the functionality
of the classes that we do have space to cover but we will endeavour to give an overview
of what is available in the Unit Tester.
Assert Class
Assertions are at the heart of unit testing and you will find that most of the test
methods that you code will require assertions to test the pre and post conditions
of the members being tested. An assertion has to evaluate to true to succeed, if
an assertion, in a test method, fails then the unit test has failed i.e. the expected
condition has not been met.
Assert is a static class, all of it's members are static and is the base class from
which the TestCase class derives. The TestCase class is the base class from which
all test cases should derive, therefore, using the Assert class methods, from within
a test case is simple and intuitive. You do not have to use the conventional
<type>.<method name> static call syntax
for calling the Assert class methods from within a test case, you can just use the
method name.
Methods are provided to test for equality, inequality, true, false, null and non-null
and there are numerous overloads for each of these tests. When an assertion fails,
the Assert class will throw an exception which will cause the test case to terminate
will a status of failed. A test case can also be explicitly failed by calling the
Assert.Fail method from within a test method. A call to the Fail method will result
in an exception being thrown which will cause the test case to terminate will a
status of failed.
Each of the Assert class methods has an overload that accepts an error message
String. If the assertion fails, the specified message will be assigned to the exception
and will, therefore, be included in the test report.
Some typical uses for assertions are as follows:
- Testing the value of properties that are initialized in a constructor
- Testing the value of properties that are initialized from configuration settings
- Testing the value of properties that are initialized from a database
- Testing the result or return value of a method
- Failing a test when a required condition is not met
TestCase Class
The Unit Tester component defines the code that tests a unit of functionality as
a 'test case'. In most cases a unit of functionality or a test case will relate
to a single class, however it does not have to, a class may have several unit tests
or test cases created for it.
TestCase is an abstract base class that derives from the Assert class, all unit
tests or test cases should derive from the TestCase class to enable their integration
with the testing framework. When executing a test run using Unit Tester the testing
framework will call each of the test methods in a TestCase derived class instance.
To be called by the testing framework each test method in a TestCase derived class
must be a public instance method with a name that begins with 'Test'. The test case
may contain other methods, private, protected or public with names that do
not begin with 'Test', any such methods will not be called by the testing framework.
For example, the public instance methods TestMethod1 and TestMethod2 will be called
by the testing framework and the methods Method1 and Method2 will not. It is also
worth noting that the Unit Tester component is case sensitive, therefore the casing
of the term 'Test' should be as shown here, public instance test methods with names
such as TESTMethod1 or testMethod2 will not be called by the testing framework.
The virtual OnTestCaseStarting method can be overridden in a derived class to perform
any initialization that is required for the test case. The testing framework calls
this method before calling any of the 'Test*' methods in the test case.
The virtual OnTestCaseComplete method can be overridden in a derived class to perform
any clean up that is required for the test case. The testing framework calls this
method when all of the 'Test*' methods in the test case have executed or immediately
after the test case has failed.
A test case can be executed either by calling the TestCase.Run method to execute
an individual test case or by adding the TestCase derived class to an instance of
the TestSuite class and then executing the test suite.
For each execution of a test case the testing framework creates an instance of the
TestResult class and associates the class with the test case by adding it to the
test case's TestResultCollection. Therefore, if a test case is executed once the
TestCase.Results property will contain a single instance of the TestResult class,
if a test case is executed twice, in the same testing session, there will be two
TestResult instances, etc.
When the execution of a test case is complete the result(s) can be retrieved, in
report form, either as a formatted String or alternatively written out to any
System.IO.TextWriter derived class. The formatting of a test case result report is defined using the
TestResultFormatter class. TestResultFormatter is a static class and is used by
the testing framework to format all test results, therefore, changing a TestResultFormatter
property will affect the result report output of all test cases.
Each 'Test*' method in a test case can contribute to the test result report by calling
one of the following protected TestCase methods:
AddComment
- Adds a comment to the test result output for a 'Test*' method.
AddException
-
Formats the exception properties and adds the details to the test result output
for a 'Test*' method. This method should not be used to add an
AssertionFailedException,
the testing framework handles this type of exception automatically. You do not have
to explicitly add any exceptions that may be thrown from the method being tested.
As long as the 'Test*' method does not handle any such exceptions they will be caught
and reported automatically by the testing framework.
ResultExpected
-
Used to specify, in the test result output, the expected result of a 'Test*' method.
An exception type or a
String message can be specified. If the actual result does
not match the expectation there could be a nonconformity in the method being tested.
ResultExpectedNoError
-
Used to specify, in the test result output, that a 'Test*' method is expected to
execute without exception. If the actual result does include an exception there
could be a nonconformity in the method being tested.
Test result output contributions are added in chronological order, therefore, it
is recommended that a consistent approach is used. For example, it makes sense to
call either the ResultExpected or the ResultExpectedNoError method at the start
of each 'Test*' method so that, when analysing the result report, it is obvious
where the nonconformities are. Comments can be added where required but it should
be noted that, by default, the testing framework automatically adds a called and
complete comment to each 'Test*' method to indicate, in the test report, the point
at which the method is called and the point at which it is complete.
TestSuite Class
The Unit Tester component also implements the TestSuite class that can be used to
contain, manage and execute a number of test cases. In a test harness that contains
several test cases it is possible to execute each of the test cases individually
or you can add each of the test cases to an instance of the TestSuite class and
execute them all consecutively by calling the TestSuite.Run method.
Instances of the TestCase class can be added to the TestSuite class either by passing
an instance of the TestCaseCollection class to the constructor or by adding TestCase
instances individually using the TestSuite.TestCases property. When a test suite
is executed, each of the TestCase instances in the suite's TestCaseCollection is
executed consecutively.
A test suite may be executed synchronously, by calling one of the Run method overloads
or asynchronously, by calling one of the BeginRun method overloads. Whether executing
a test suite synchronously or asynchronously, a test suite may be executed once,
a specified number of times or for a specified period of time.
When running a test suite for a long period, either by specifying the number of
runs or by specifying a time period for the run, the ProgessEvent can be used to
provide feedback in the test harness user interface. A test suite can be configured,
using the FileFlushConfiguration class, to write out the result reports at intervals
during a long test run and to clear them from memory once they have been flushed.
Conclusion
A unit testing framework can't, unfortunately, code the test methods for you, however,
it can undoubtedly reduce the workload by providing a consistent structure for your
tests and a mechanism for running the tests and reporting the results. The Blayd
Software Unit Tester component implements a programmatic approach by providing methods
that you call from your test methods to interact with the testing framework. It
can also be used with any .Net development environment and your preferred test harness
user interface.
Copyright ©Blayd Software Limited 2007-2010. All rights reserved.