Testing software has many meanings, which are often confused. In the last decade or so, we’ve seen agile development methodologies go mainstream in the modern enterprise. These methodologies are often misunderstood and misused. Some times they assume you have clean testable code. Often, when dealing with legacy applications, you simply don’t.
I’ve encountered legacy Java code which had methods over 2,000 lines long. One method, 2,000 lines of code. A clear violation of the Single Responsibility Principle and other Principles of Object Oriented Design. How do you unit test this method? Answer is, you can’t. It’s pretty much impossible to ensure proper unit test coverage of a method with 2,000 lines of code.
You can read a lot about agile software development and testing software, but one often overlooked fact is you’re code must first be testable. If you don’t have clean testable code, you will be limited in the benefits you can receive from automated testing.
The various types of testing are often misused in discussion. I once overheard an executive say println statements were unit tests. I’m sorry, they are not. println statements are NOT UNIT TESTS.
I’d like to offer my definition of categories for testing software.
The phase ‘Unit Test’ is often misused. People often refer to tests as “Unit Tests” when they are actually a different type of test. Characteristics of Unit Tests:
- Unit tests are small, short, lightweight tests.
- Should roughly focus on one public method call of the class under test.
- Should be ‘unit-y’.
- Should not interact with the database, any external services, or bring up a Spring Context.
Remember keep your unit tests light and fast. JUnit is the most popular tool for doing unit tests in Java. TestNG is also very popular.
Integration Tests are a broader category of automated software tests. Often if your test fails the characteristics of a ‘Unit’ test, its actually a integration test. Characteristics of Integration Tests:
- Tests the interaction of one or more classes.
- Might interact with a database.
- Uses a Spring Context.
- Talks to a web service or message queue.
- Runs in a container, such as tomcat.
- Needs a web context.
Because Integration Tests are doing more and interacting with more stuff, they are expected to run longer than unit tests. A true unit test will complete in a fraction of a second, while an integration test can take several seconds to complete.
JUnit is also a popular tool for writing integration tests. Keep in mind, just because you’re using ‘JUnit’, it does not mean your test is a ‘Unit Test’. Once you interact with a database or bring up a Spring Application Context, your test is an Integration Test.
Functional tests are as they sound. ‘Functional’. When running functional tests, it’s common for your code to be deployed in its run time configuration and you’re simulating the actions of the functional client. Characteristics of Functional Tests:
- Your code is running as is deployed. Possibly in an embedded Tomcat instance or deployed to a development server.
- Your test client is acting as a functional client. You might be using Selenium to drive a web browser to test the web pages being rendered by your software, or maybe acting as a web service client to test a web service you developed.
Functional tests are by far the slowest, yet most complex tests. They build upon your integration tests, and automate the testing of your software as a client would interact with it.
I’ve presented some basic rules of thumb when it comes to how to describe your tests. In the real world, you can easily get into shades of gray. And it’s easy to misuse these terms. I’m guilty of it. I often mistakenly refer to integration tests as unit tests.
When you’re testing your software, keep in mind each type of test has its role. It would be a mistake to think you need all unit tests and no functional tests. Or to think that your application is some how special and you can get proper code coverage from just writing functional tests.
In reality, you need a combination of all three. The majority of your tests should be unit tests, then integration tests, then the fewest (yet still important), functional tests.