Skip to main content

Unit tests are not integration tests

Posted by johnsmart on June 12, 2008 at 5:09 PM PDT

The classic difference between integration and unit testing is that unit tests run in isolation, or near-isolation. Using an in-memory database for DAO unit tests. Using mocked-out components for the other layers. Integration tests, on the other hand, test the whole stack. If you're using Spring, you inject the whole Spring context and try to flush out any configuration issues, or integration issues between the various layers.

Integration tests tend to run slowly. Unit tests run fast. You don't want to slow down your quick coding turn-around time by running the integration tests every time - run them less frequently than the unit tests (there's usually no need to retest the Spring configuration files if you've only changed a couple of lines of code - you'll do this before you commit, anyway).

Integration tests often have more of a BDD feel to them. They simulate, to some extent, expected user behavior. Unit tests can do this too, but integration tests do so at a higher level. This means they can be candidates for reuse later on for performance and scalability tests. This is even better if you add some parameters to your integration tests (e.g. by using JUnit 4.4 Theories) - this makes them much easier to reuse for more heavy-weight testing. I'll discuss this in more detail in the next installment.

Personally, I materialize this distinction using naming conventions (e.g. ClientTest for a unit test, ClientIntegrationTest for an integration test). Then it's pretty easy to get each category of test to run at different times in the build cycle. In maven, it's as simple as 20 or so lines of XML :-), as shown here:

            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <skip>true</skip>
                </configuration>
                <executions>
                    <execution>
                        <id>unit-tests</id>
                        <phase>test</phase>
                        <goals>
                            <goal>test</goal>
                        </goals>
                        <configuration>
                            <skip>false</skip>
                            <excludes>
                                <exclude>**/*IntegrationTest.java</exclude>
                            </excludes>
                        </configuration>
                    </execution>
                    <execution>
                        <id>integration-tests</id>
                        <phase>integration-test</phase>
                        <goals>
                            <goal>test</goal>
                        </goals>
                        <configuration>
                            <skip>false</skip>
                            <includes>
                                <include>**/*IntegrationTest.java</include>
                            </includes>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

A bit cumbersome, I admit. But, with this, you can use the standard Maven test command to run uniquely the unit tests:
mvn test

Then, to run just the integration tests, you run:
mvn integration-test

Naturally, you can do the same thing at a lower level in Ant with the JUnit Ant task.

In TestNG, you can use test groups - this is one of the nice features of TestNG compared to JUnit 4.4.

Next time: Performance Testing is not Scalability Testing

Comments

The whole point of Maven's lifecycle is that it defines a fixed sequence of phases. The main build lifecycle goes something like validate -> compile -> test -> package -> integration-test -> verify -> install -> deploy unit tests should run in the test phase, integration tests in the integration-test phase. Because the integration-test phase may require the packaged artifact to be deployed to a server, you use pre-integration-test to install the packaged artifact into the "test-container" and post-integration-test to tear down the "test-container". The integration-test phase should _never_ fail, instead, the results of integration tests should be checked in the verify phase (in order to ensure that the post-integration-test gets to run) If you are running integration tests, therefore, you should type "mvn verify" not "mvn integration-test", oh and it's shorter too!

I have the same issue: unit-tests run before integration-tests.

What I get is that running "mvn integration-test" first it launches all tests (like in mvn test) and then runs just the **/*IntegrationTest

That's also my approach, but because I'm lazy in writing I only use ITest for integration tests. One thing to mention is the possibility to also use these integration tests for complete integration tests in a client/server architecture. Maven provides the phases pre-integration-test and post-integration-test where the server can be started and the application be deployed or undeployed and stopped.