In a recent article, Michael Feathers enumerates a set of rules defining unit
tests:

A test is not a unit test if:

  • It talks to the database
  • It communicates across the network
  • It touches the file system
  • It can’t run at the same time as any of your other unit tests
  • You have to do special things to your environment (such as editing
    configuration files) to run it.

These are indeed very surprising rules, because a unit test is
traditionally defined as a set of test that exercises a single class in
isolation of all others
.  With this definition, if the responsibility
of your class is to verify that a file contains the appropriate data, it
will obviously violate one of Michael’s laws.  Yet, by my definition,
this test still qualifies as a unit test.

Another interesting observation is that based on my experience, 90% of the
tests that I see written with JUnit on a daily basis are not unit tests.

Anyway, these semantic games are fun to play but not very useful at the end
of the day.  The bottom line is that the success of JUnit has shown that the line
between unit and non-unit tests is increasingly being blurred.

However, when I write a test, I don’t really care if it’s a unit test or a functional
test.

What I care about, though, is its categorization:  is it a database
test?  A front-end test?  A network test?  Does it run quickly? 
Slowly?

This is why groups are so important in tests, because they give
the developer maximum flexibility to define additional information on the tests
they write, something that packages or class naming cannot give you (a test
class can obviously not belong to two packages).  And obviously, these
categories are not mutually exclusive:  a database test can run fast or
slow, as can a network test.

Here is what it looks like with
TestNG
:

@Parameters({ "name" })
@Test(groups = { "fast", "database" })
public void insertOneRow(String name) { … }

@Test(groups = { "slow", "database" })
public void verifyIntegrity() { … }

Do you want a quick sanity check before committing code? Run all
the fast tests, regardless of whether they exercise the database or the
network.

Do you want a full functional coverage to make sure absolutely nothing is broken
in your product? Run the slow tests (or the integration ones).
Did you just modify a schema and you want to make sure you didn’t break
anything? Just run the database tests.

Groups are a very powerful and addictive feature once you start using them…