Skip to content

Kotlin test fixture generator from existing database schema

License

Notifications You must be signed in to change notification settings

maeharin/factlin

Repository files navigation

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