/factlin

Kotlin test fixture generator from existing database schema

Primary LanguageKotlinApache License 2.0Apache-2.0

Download CircleCI codecov

factlin

factlin is Kotlin test fixture generator from existing database schema.

This gradle plugin generate fixture factory class and insert helper method (currentlly support only Ninja-Squad/DbSetup)

example

From this database schema

CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name VARCHAR(256) NOT NULL,
  job VARCHAR(256) NOT NULL DEFAULT 'engineer',
  status VARCHAR(256) NOT NULL DEFAULT 'ACTIVE',
  age INTEGER NOT NULL,
  score NUMERIC NOT NULL,
  is_admin BOOLEAN NOT NULL,
  birth_day DATE NOT NULL,
  nick_name VARCHAR(256),
  created_timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL,
  updated_timestamp TIMESTAMP WITHOUT TIME ZONE
);

COMMENT ON TABLE users IS 'user table';
COMMENT ON COLUMN users.id IS 'primary key';
COMMENT ON COLUMN users.name IS 'user name';
COMMENT ON COLUMN users.job IS 'job name';
COMMENT ON COLUMN users.status IS 'activate status';
COMMENT ON COLUMN users.age IS 'user age';
COMMENT ON COLUMN users.score IS 'game score';
COMMENT ON COLUMN users.is_admin IS 'user is admin user or not';
COMMENT ON COLUMN users.birth_day IS 'user birth day';
COMMENT ON COLUMN users.nick_name IS 'nick name';

Generated Kotlin code is like this

data class UsersFixture (
    val id: Int = 0, // primary key
    val name: String = "", // user name
    val job: String = "", // job name
    val status: String = "", // activate status
    val age: Int = 0, // user age
    val score: BigDecimal = 0.toBigDecimal(), // game score
    val is_admin: Boolean = false, // user is admin user or not
    val birth_day: LocalDate = LocalDate.now(), // user birth day
    val nick_name: String? = null, // nick name
    val created_timestamp: LocalDateTime = LocalDateTime.now(), 
    val updated_timestamp: LocalDateTime? = null 
)

fun DbSetupBuilder.insertUsersFixture(f: UsersFixture) {
    insertInto("users") {
        mappedValues(
                "id" to f.id,
                "name" to f.name,
                "job" to f.job,
                "status" to f.status,
                "age" to f.age,
                "score" to f.score,
                "is_admin" to f.is_admin,
                "birth_day" to f.birth_day,
                "nick_name" to f.nick_name,
                "created_timestamp" to f.created_timestamp,
                "updated_timestamp" to f.updated_timestamp
        )
    }
}

Use in your test (with Ninja-Squad/DbSetup)

dbSetup(dest) {
    deleteAllFrom(listOf("users")) 
    
    // using generated codes. this codes insert datas to your database
    insertUsersFixture(UsersFixture(id = 1, name = "user1"))
    insertUsersFixture(UsersFixture(id = 2, name = "user2", is_admin = true))
}.launch()


...your db test

benefit

  • type and null safe db test fixture generation (no more excel and insert into...)
  • help to generate similar test fixtures from another fixture (generated fixture class is Kotlin data class)

supported databases

  • PostgreSQL
  • MariaDB

how to use

create tables in your db

CREATE TABLE ...

build.gradle

buildscript {
    repositories {
        jcenter() // factlin is published at jcenter
    }
    dependencies {
        classpath 'com.maeharin:factlin:0.1.1'
        classpath "org.postgresql:postgresql:42.1.4"
    }
}

apply plugin: 'kotlin'

// factlin config
apply plugin: 'factlin'
factlin {
    dbUrl = "jdbc:postgresql://{DB_HOST}/{DB_NAME}"
    dbUser = "{DB_USER}"
    dbPassword = "{DB_PASS}"
    dbDialect = "postgres"
}

repositories {
    mavenCentral()
}
dependencies {
    compile "org.postgresql:postgresql:42.1.4"
    testCompile 'com.ninja-squad:DbSetup-kotlin:2.1.0' // generated code depends on dbsetup
}

generate codes

./gradlew factlin

use generated codes for your db connection test (ex: JUnit)

class SampleTest {
    val dest = DriverManagerDestination("jdbc:postgresql://{DB_HOST}/{DB_NAME}", "DB_USER", "DB_PASS")

    init {
        Class.forName("org.postgresql.Driver")
    }

    @Test
    fun testInsertUser() {
        dbSetup(dest) {
            deleteAllFrom(listOf("users"))
            insertUsersFixture(UsersFixture())
        }.launch()
        
        val stmt = dest.connection.createStatement()
        val rs = stmt.executeQuery("select * from users")
        
        assertTrue(rs.next())
        assertEquals("", rs.getString("name"))
    }
}

run test

./gradlew test

done!

customize

you can customize default behavior at build.gradle like this:

apply plugin: 'factlin'
factlin {
    dbUrl = "jdbc:postgresql://{DB_HOST}/{DB_NAME}"
    
    dbUser = "{DB_USER}"
    
    dbPassword = "{DB_PASS}"
    
    // postgres or mariadb
    dbDialect = "postgres"
    
    // output directory of generaged code
    // defult: src/test/kotlin/com/maeharin/factlin/fixtures
    fixtureOutputDir = "src/test/kotlin/com/example/myapp/fixtures"
    
    // package name of generated code
    // default: com.maeharin.factlin.fixtures
    fixturePackageName = "com.example.myapp.fixtures"
    
    // custom template(FreeMarker) path
    // default: nothing (use factlin default template file)
    // https://github.com/maeharin/factlin/blob/master/src/main/resources/factlin/class.ftl
    fixtureTemplatePath = "src/test/resources/factlin/class_template.ftl"
    
    // exclude table names
    // default: nothing
    excludeTables = ["payment_p2007_01", "payment_p2007_02", "payment_p2007_03", "payment_p2007_04", "payment_p2007_05", "payment_p2007_06"]
    
    // include table names
    // default: all tables
    includeTables = ["users", "film"]
    
    // true if delete output directory before code generation 
    // * recommended to be true for ease of maintenance
    // default: false
    cleanOutputDir = true
    
    // override specific default value
    // format: [tableName, columnName, defaultValue]
    // default: nothing
    customDefaultValues = [
        ["users", "job", "\"engineer\""],
        ["users", "status", "\"ACTIVE\""],
    ]
    
    // override database column type to kotlin type mapping
    // format: [databaseColumnType, KotlinType]
    // see default mapper: https://github.com/maeharin/factlin/blob/master/src/main/kotlin/com/maeharin/factlin/core/kclassbuilder/KClassBuilder.kt#L59
    customTypeMapper = [
        "year": "SHORT", // treat custom type [year] as SHORT
    ]
    
    // name of target database schema
    // this is not working for no schema support database(ex: mariadb, mysql...)
    // default: null (include all schemas)
    schema = "public"

    // if true, use camel-case for fixture class property  name.
    useCamelCase = false
}

sample project

how to develop

unit test

./gradlew test

integration test

docker-compose up
./init-db.sh
./integration-test.sh

publish

// .env is secret
source .env
./gradlew clean build bintrayUpload --info