by Brendan Enrick
Feedback
|
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days):
41295/
115
|
|
|
Introduction |
Testing is an important practice for software developers.
There are plenty of reasons to write tests and maintain them. In this post I've
aggregated a collection of blog posts of mine, which cover some tips for
testing software.
References
·
Time-Tested
Testing Tips - Part 1
·
Time-Tested
Testing Tips - Part 2
·
Time-Tested
Testing Tips - Part 3
|
Tests as Documentation |
I've said plenty of times in the past that the tests you
write become excellent documentation which can be used to demonstrate how
different aspects of your system should be used. This is very useful for
someone joining a project. If it is well-documented in its tests, learning how
to work with the code is as easy as reading through the tests and seeing how
the different objects are intended to interact. If you can't do this, you're
probably not testing well.
A good measure of your application's testing is to have
someone learn to use the system based solely on the tests. If the tests are
written well enough, someone will be able to figure out how everything works
and interacts.
This is one of my primary goals when testing an application.
I want to make sure that things are clear, because when I come back to this
code a month from now I will be that new developer on the project. I will need
to know how to use the classes, interfaces, etc. and having it documented in
working cases goes a long way.
|
How many tests should I write? |
This is one of the most common questions asked by new
testers. For now I will take the cowardly approach and say, "it
depends". I've read posts from people touting the number of tests they've
written. This is a new metric as if "line count" wasn't already bad
enough. There is a balance here that we are looking for. Just with the line
count.
If your line count is small you're application probably
doesn't do very much and if it is large then your application might be
cumbersome and difficult to maintain. When you don't have many tests your code
is more prone to errors, but if you've written too many tests maintainability
disappears. But wait! Didn't I say earlier that tests make code more
maintainable. Well, yes I did, but if you write too many it becomes difficult
to change the code. If you test every single possible minute little thing in
your application you're going to have a heck of a time changing anything.
When you're writing tests make sure you
have just enough to give yourself confidence that your code is working.
I highly recommend against ever setting code coverage or
test count goals. If you set goals for these you're just creating incentives to
write more tests than is required. Too many tests can create the same problems
as too much code. Why? Because tests are code.
|
Test Driven Development |
Yes, I am going to bring this up. Plenty of people have
latched onto the idea that testing code helps make code better. A lot of people
even believe that it makes the process of writing the code faster. I see a lot
of people who are reluctant still to use test driven development. I know I am
throwing around a buzzword… or maybe I mean buzzphrase since that’s actually
three words.
I don’t plan on doing TDD justice here since I am trying to
keep these tips relatively short. I just hope this is enough to inspire people
to go read, learn more, and try some test driven development. I am going to
give some quick reasons why developers testing their code should write the
tests first.
·
You will not want to go back and write it later.
Sometimes you are going to go back and write the tests for the code you have
already written, but what happens if you decide you’re going to write a lot of
code and then go back and add the tests. Now you’re talking about a lot of
testing all at once. There is a good chance you’ll cut corners and maybe skip
entire aspects of the testing. I know I would be tempted.
·
How do you know what kind of interface your new
code needs if you’ve never used it? If you start by trying to use some
code that does not exist yet, you’ll be deciding how you would want to use it.
Now you’re probably thinking you could do that anyway. If you write code to use
some new feature you will know the design is easy to use and work with. You
just made it up and used it in the test. If you didn’t write the test you had
to guess what interface you will want later.
|
Organize, Refactor, and Take Care of Your Tests |
I recommend you repeat this daily, “My test code is just as
important as my production code”. I think this is a very good point to remember
when you’re writing tests. All of those principles you apply when writing
production code should be followed with test code. DRY, SOLID, YAGNI, etc. are
all important even with the testing code.
Obviously duplicating code can make your tests difficult to
maintain. What if some business logic changes? If you were repeating yourself
you now have the fun task of going through a dozen tests changing each one, but
if you had not repeated the same code you might have been able to update one
location in the test code. A lot of people are concerned when the line count of
a single file gets large and they will refactor it into multiple manageable
files. This same policy should apply to test classes. If you’ve ever gone into
a test class with way too many classes, you probably know how difficult it can
be to maintain.
Tests exist to make development easier, and if they become
difficult to maintain then something needs to change. I certainly don’t
advocate spending large amounts of time refactoring the tests, but since they
are supposed to increase the longevity of the application they must also be
maintained.
|
Adjust Your Style to Your Test Framework |
I obviously recommend that everyone use a testing framework.
There are plenty of them out there, and they supply a great deal of the tools
you’ll need for testing. They come in all languages, flavors, and colors. You
might look into these NUnit, MSTest, JUnit, CppUnit. What is important is that
you make sure you know how your tools work so you can test best with them.
I will elaborate on a couple of examples and perhaps down
the road I’ll give some more specific examples.
Some tools show you a list of test names, and only minor
details about why the test failed. For this you usually need to go the
description view or something similar. In these cases it is important to name
your tests effectively.
As an example if I have a method called Add and it takes two
parameters a and b. I might write a test for that method. If I name my test
TestAdd, and that test fails you know something is wrong in that method, but
you do not know what failed. If I had instead made a few more specific methods
you could glean more information from the test having failed. Some examples of
tests I might create are AddTwoZerosShouldBeZero,
AddNumberToZeroShouldBeTheNumber, AddPositiveNumbersTest,
AddNegativeNumbersTest, etc.
Some parts I would lump together like positive numbers and
negative numbers. It is important to handle a couple of different scenarios as
well as the edge cases. I could have done negative and natural numbers and that
would cover all numbers, but I wanted to make sure the edge case, zero, was
handled correctly, so I test it separately.
People can argue back and forth all day long about whether
you should have a lot of small tests or group them together, but this is what
has worked well for me in the past and I hope it works well for you also.
|
Keep Test-Relevant Details Visible |
I've said in the past to treat test code just like any other
code. However, there are a few reasons to break from this rule. One is the
context switching which will be caused by extracting information which is
important to a test. Let me explain what I mean with two examples.
Example Using Helper Method
[Test]
public void CalcInterestRoundsToTenthPennies()
{
decimal initialMoney = 100.00;
decimal expectedInterest = 33.333
InterestCalculator ic = GetTestInterestCalculator();
decimal actualInterest = ic.CalcInterest(initialMoney);
Assert.AreEqual(expectedInterest, actualInterest,
"CalcInterest did not round to a tenth of a penny correctly");
}
Example Without Helper Method
[Test]
public void CalcInterestRoundsToTenthPennies()
{
decimal initialMoney = 100.00;
decimal interestRate = 0.3333333333;
decimal expectedInterest = 33.333
InterestCalculator ic = new InterestCalculator(interestRate);
decimal actualInterest = ic.CalcInterest(initialMoney);
Assert.AreEqual(expectedInterest, actualInterest,
"CalcInterest did not round to a tenth of a penny correctly");
}
Notice here that you can tell a lot more about what is going
on in the second one, because you know what percentage is being used to
calculate. You cannot tell that the first one is accurate since you can't see
the percent. I recommend trying to keep the amount of values to a minimum and
if you're writing your code well you'll have few enough dependencies that you
will not have a problem. These helper methods as you can see make it so you
will need to go to another location to see what was initially created. In some
instances they are useful, but use them sparingly as they might hide details.
|
Do Not Unit Test Third Party Frameworks |
Make sure that what you're testing is part of your code and
not someone else's. If you're testing code you have no access to you better not
be keeping that around as an automated test. You're wasting your time if you're
creating automated tests for this code. If you find a bug in the code you can
tell someone about it, but you probably can't fix the code. If it is an open
source project you're testing then go test the code in their test
library and fix issues you find. You will be doing them and yourself a favor.
Don't clutter your own test library.
|
Write Automated Tests Whenever You Are Curious |
If I ever wonder how some piece of code or a class works or
something, I could go write a simple application and test something out. I
certainly do not waste my time though. It tends to be much faster just to write
a test to answer my question or learn something new. If I want to try something
out I just write a test. If it is an internal piece of code I go look at the
existing tests, and if it is not I write a temporary one just to try something
out.
Unit tests are quite useful for this sort of thing, and
since I have keyboard shortcuts mapped to run them they're very fast. If you
really want to test a lot, you need to start using them all the time. They
become very easy to come up with and write once you practice them enough.
|
Conclusion |
Testing isn't hard to learn, it is hard to master. Remember
that testing is about designing great, maintainable, working software. If
you're just testing to test, you're going about it the wrong way. Testing is a
powerful tool, but it is also a great strategy for designing excellent
software. Keep designing your software through testing, and you will see what I
mean.
|
|
|
|
|
|