Unit Testing Succinctly: Other Purposes

This is an extract from the Unit Testing Succinctly eBook, by Marc Clifton, kindly provided by Syncfusion.

Unit testing is also valuable for other purposes.

As Examples of Usage

One of the side benefits of unit testing is that it creates a large code base exemplifying how to use the code. For example, the code we saw earlier:

documents an expected valid use case for the command line parser. Also consider writing unit tests—not only for your own code, but also to provide examples for third-party libraries. (See subsequent examples and the interesting things revealed about the Rectangle struct.)


Black Box Testing

Black box testing assumes that you do not know anything about the internals of the class or service and are verifying its behavior strictly from the publicly exposed interfaces. This is frequently necessary when you don’t have the code available. For example, when working with a record management company, we were required to use a web service provided by a government agency to update records. By writing unit tests for the web service, we were able to prove that the documentation provided to us did not result in the expected behavior of the web service.

You might use this technique as well when working with code provided by different departments. For example, the database group might have its own white box unit testing; however, you should also verify that from a black-box perspective, the triggers and constraints have been programmed correctly by inspecting the result of transactions from the functionality that is exposed to you.


Test Your Assumptions

Unit testing can be a simple way to put together some tests regarding our assumptions about an API. Let’s take the System.Drawing.Rectangle structure and test some seemingly reasonable assumptions about the implementation.

Test Constructor Assumptions

There are two Rectangle constructors: one having Point and Size parameters, the other having x, y, width, and height parameters. The documentation makes no indication of whether the size (the width or height) must be positive, so let’s write a test to verify that we can construct a rectangle with a negative width or height:

All we are doing here in this test is verifying that no exceptions are thrown when we construct the rectangle, and indeed, this is the case:

Rectangle Constructor Test
Rectangle Constructor Test

Test Assumptions Regarding Property Values

Now let’s test our assumptions about certain properties. The properties Top, Left, Bottom, and Right are described as (see
http://msdn.microsoft.com/en-us/library/system.drawing.rectangle.aspx):

Top: Gets the y-coordinate of the top edge of this Rectangle structure.

Left: Gets the x-coordinate of the left edge of this Rectangle structure.

Bottom: Gets the y-coordinate that is the sum of the Y and Height property values of this Rectangle structure.

Right: Gets the x-coordinate that is the sum of X and Width property values of this Rectangle structure.

So, with the preceding rectangle, with a negative width and height, and therefore having coordinates [(-4, -6), (0, 0)], we would make the following assumptions:

However, this is not the case:

Testing Assumptions about Rectangle Properties
Testing Assumptions about Rectangle Properties

In fact, the determination of top and bottom appears totally arbitrary as well, as I have run tests on exactly the same rectangle dimensions and observed different results in the Top and Bottom property values.

Test Assumptions about Method Results

The MSDN documentation states that the Rectangle.Intersect method:

  • Returns a third Rectangle structure that represents the intersection of two other Rectangle structures.
  • If there is no intersection, an empty Rectangle is returned.

Therefore, we can construct a simple test:

with the result:

Testing Our Assumptions about Method Returns
Testing Our Assumptions about Method Returns

This informs us that our expectation, based on the documentation, is incorrect.

In Conclusion

Unit testing is an important tool in the testing process. While integration and usability testing are often more customer-centric (reporting, milestones, verifying high-level requirements), unit testing is the first line of defense for a programmer, his or her team, and the team managers. If used judiciously (remember, you are not aiming to create thousands of pretty green lights), it can be a cost-effective way of verifying the computational correctness of the code and for re-creating bugs and verifying that they have been fixed.

However, good unit testing practices require a disciplined approach, a commitment to the time and effort required to implement and maintain the tests, and, from a coder’s perspective, it also requires good programming practices and often enforces architectural decisions. The latter may fly in the face of “just get it done” constraints (which may be quite legitimate), and may potentially impact performance. On the upside, the programming practices and architectures that unit testing forces one to use are often of great benefit to the entire application development process, thus reducing costs and improving maintainability, not because the code is unit tested, but because the code is written better so that it can be unit tested.

Tags:

Comments

Related Articles