Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 9 Next »

The Liquibase code contains both unit tests and integration tests. This document explains the basics of creating Unit and Integration tests that will be used by the automated build process to validate Liquibase and reduce regressions.

Unit Tests

Unit tests test functionality in isolation, without any external systeminteraction. Because there are no external dependencies, these tests need no additional setup and run fast.

Liquibase Unit tests are written using the Spock testing framework.

Naming and Location of the Unit Test

Unit tests are written at the Java class level. There is a 1-1 mapping between the unit test class and the Java class being tested. The unit test class has the same package and name as the Java class, but with Test.groovy appended. All test classes are stored using the src/test/groovy structure expected by Maven.

Here is the example for liquibase-core/src/main/java/liquibase/util/StringUtil.java

  1. Replace main/java in the file path with the new substring src/test/groovy

    • liquibase-core/src/main/java/liquibase/util/

    • liquibase-core/src/src/test/groovy/liquibase/util/

  2. If a unit test doesn’t already exist, create a new unit test class with the same base filename as the Java class and append Test.groovy

    1. liquibase-core/src/main/java/liquibase/util/StringUtilTest.groovy

  3. The next step is to create the actual test

Creating the Unit Test

In this example, we added a new method in this class named public static String StringUtil.trimToNull(String). The goal of the method is to return a new string with leading and training whitespace removed. If the resulting string is empty, then return null.

The Java Class to Test

See the actual Java class in GitHub -https://github.com/liquibase/liquibase/blob/master/liquibase-core/src/main/java/liquibase/util/StringUtil.java- or the edited example below:

liquibase-core/src/main/java/liquibase/util/StringUtil.java

package liquibase.util;

import liquibase.ExtensibleObject;

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Pattern;

/**
 * Various utility methods for working with strings.
 */
public class StringUtil {
    private static final Pattern upperCasePattern = Pattern.compile(".*[A-Z].*");
    private static final Pattern lowerCasePattern = Pattern.compile(".*[a-z].*");
    private static final SecureRandom rnd = new SecureRandom();
    /**
     * Returns the trimmed (left and right) form of the input string. If the string is empty after trimming (or null
     * was passed in the first place), null is returned, i.e. the input string is reduced to nothing.
     * @param string the string to trim
     * @return the trimmed string or null
     */
... 
    public static String trimToNull(String string) {
        if (string == null) {
            return null;
        }
        String returnString = string.trim();
        if (returnString.isEmpty()) {
            return null;
        } else {
            return returnString;
        }
    }
...
}

The Unit Test

See the actual Unit Test class in GitHub -https://github.com/liquibase/liquibase/blob/master/liquibase-core/src/test/groovy/liquibase/util/StringUtilTest.groovy- or the edited example below:

1. In the liquibase-core/src/src/test/groovy/liquibase/util/ directory, create or edit the StringUtilTest.groovy file. Add the following new test method to test the trimToNull() method in the Java class.

The test method name should descriptive of what is being tested. At a minimum it should be the name of the method under test, but ideally it should describe what is being tested by the method.

class StringUtilTest extends Specification {
...
    def "trimToNull"() {
        
    }
...    
}

2. Add a simple assertion. In this case, there is no setup needed so a simple “expect” works best. If you are new to the Spock testing framework and need more information on the syntax below, please read Spock Test Framework - Getting Started.

Focus on test readability. What is being tested and why should be immediately obvious to anyone looking at the test.

    def "trimToNull"() {
        expect:
        StringUtil.trimToNull("test string") == "test string"
    }

3. Refactor and expand your test with more test cases. Try to think of test for all the edge cases. Specify the test variations in the where clause.

Use more “where” data over additional tests. Before adding a new test method, look at the existing tests and see if any can be refactored to include your scenario.

@Unroll("#featureName: '#input'")
def "trimToNull"() {
    expect:
    StringUtil.trimToNull(input) == expected

    where:
    input                     | expected
    "test string"             | "test string"
    "test string   "          | "test string"
    "   test string"          | "test string"
    "   test string     "     | "test string"
    "test    string"          | "test    string"
    ""                        | null
    "    "                    | null
    "\n\r\ttest string\r\n\t" | "test string"
}

Running and Testing the Unit Test

The Spock framework is an extension of JUnit. During development it is normally executed through your IDE just like any other test. Your IDE will let you choose which test (or all the tests) you want to run.

You are also able to run all unit tests via the maven command - mvn test.

<provide step by step on running the command via maven - please also include output>

Integration Tests

The goal of integration tests are to test:

  1. How various Liquibase systems work in concert with each

    1. Including end-to-end scenarios

  2. How Liquibase interacts with external systems (especially databases)

Integration Database Configuration/Startup

Being able to connect to databases is central to running integration tests in Liquibase, but we also recognize that developers rarely have all possible databases running and available in their system. Therefore, we have a system that is able that is able to find your available databases and does not throw misleading errors when you do not have a database available.

Liquibase will automatically run tests against databases it is able to connect to based on the configuration files (see below) and will skip tests against databases it is not able to connect to.

Configuration Files

The primary configuration for the database setup is https://github.com/liquibase/liquibase/blob/master/liquibase-integration-tests/src/test/resources/liquibase/liquibase.integrationtest.properties . That file contains the default configuration for the database connections, which can be started using our docker-compose file (see below). The file contains a default username/password we generally use for all databases (username=lbuser, password=LiquibasePass1) as integration.test.username and integration.test.password and then per-database configuration options with a integration.test.DB_NAME prefix. For example, derby configs start with integration.test.derby... and oracle configs start with integration.test.oracle… such as integration.test.derby.url and integration.test.oracle.url.

If you ever need to test against a connection with a different configuration, you can create a liquibase.integrationtest.local.properties file in the same resources directory as liquibase.integrationtest.properties and override ONLY the configuration values you need. For example, if you are running against an oracle database on a different system, you can set integration.test.oracle.url=jdbc:oracle:thin:@//192.168.1.158/mydb. The liquibase.integrationtest.local.properties is not checked into git because it is local to your environment.

Docker Databases

To standardize configuration/installation of different databases, we have a docker-compose file at https://github.com/liquibase/liquibase/blob/master/.travis/databases.docker-compose.yml. The databases started through this setup will match the default liquibase.integrationtest.properties settings and are used as part of the buildserver.

We currently only have mysql configured, but will be adding more soon.

To start the test databases:

  1. Install Docker and docker-compose

  2. from the .travis directory, run:

    1. docker-compose up

      1. Starts all databases

    2. docker-compose up mysql

      1. Starts just the mysql database

  • No labels