Fixture factory in Kotlin

Overview

KFactory

Build

Create best-in-class factories for your synthetic data in Kotlin.

  Test fixtures
  DB seeding
  Feature demos
  Pre-production environments


About KFactory

Why synthetic data?

Chances are that synthetic data are going to be a major painpoint for your project sooner than later.

  • Local development
  • Unit tests
  • Exploratory testing
  • Showcase a feature
  • Pre-production environments

Are you going to need synthetic data for any of the above?

Yes! - Probably for all of them

Synthetic data strategies

There are 3 major ways to create synthetic data that we are aware of:

  1. Static fixtures
  2. Factories
  3. Production copies

💙  Static fixtures usually live in plain YAML or JSON files. They are typically fed directly to the underlying database, skipping standard validation/integrity checks for simplicity and performance. Since data integrity is not a priority certain things like exploratory testing and refactorings become much harder. Finally, due to their hardcoded nature, generating large volumes of data is out of question. That's why we typically recommend static fixtures only for small apps.

💚  Production copies provide a great way to populate a system with data. You typically get valid, large and versatile enough data to cover most of use cases. BUT if you think about it... usually production data contains sensitive data and you'll need some sort of obfuscation/anonymization before you load them into a develoment/test system. That's 🤯 - especially when there are multiple databases involved.

Some other times, production copies can be too small - during the early days of a product - or too large to be of practical use. And certainly, you cannot really write unit or functional tests on production copies cause they are dynamic and unpredictable. We typically recommend production copies for populating pre-production environments.

❤️  Factories or dynamic fixtures if you like, directly produce models from within our domain. Models that can be validated and stored in the underlying database with all integrity checks in place. A well made set of factories, is one that captures all the necessary abstractions that let you create data for a certain business scenario in a few lines of code. We typically recommend factories for most use cases, but primarily for creating test fixtures and populating local development environments.

Why KFactory?

Other ecosystems have robust synthetic data solutions for some time now, mostly inspired from Ruby's amazing FactoryBot.

KFactory is also inspired by FactoryBot - in a Kotlin idiomatic way.

  • Built-in helpers
  • Composable factories
  • Traits
  • Lazy sequence builds

Installation

KFactory is published on mavenCentral. In order to use it just add the following dependency:

implementation("io.github.bluegroundltd:kfactory:1.0.0")

Usage

You can find a lot of factory examples inside examples directory!

API Reference

API reference is available under this link!

Creating a new Factory

When you have a domain entity that you want to create a Factory for you can start by doing the following:

class AddressFactory : Factory<Address> {
  override fun produce() : Address = Address()
}

Introducing Factory traits

A lot of times we want to produce fixtures from factories, but we only need to change only a few of their attributes/characteristics.

For example:

class AddressFactory(
  private var city: String = "city",
  private var state: String = "state",
) : Factory<Address> {

  fun withCity(city: String) = apply {
    this.city = city
  }

  fun withState(state: String) = apply {
    this.state = state
  }

  override fun produce() : Address = Address(
    city = city,
    state = state
  )
}

Now if we want to produce several instances of Address that will retain city but will have another specific value for state, we can create a new FactoryTrait that we will later apply to that Factory.

object CaliforniaTrait : FactoryTrait<AddressFactory> {
  override fun modifyWithTrait(factory: AddressFactory): AddressFactory = factory
    .withState(state = "California")
}

Enhancing a Factory with Traits

In order to enhance our Factory with a FactoryTrait like we previously saw, we need to use the TraitEnhancedFactory marker interface.

For example consider the following:

class AddressFactory(
  private var city: String = "city",
  private var state: String = "state",
) : Factory<Address>, TraitEnhancedFactory {

  fun withCity(city: String) = apply {
    this.city = city
  }

  fun withState(state: String) = apply {
    this.state = state
  }

  override fun produce() : Address = Address(
    city = city,
    state = state
  )
}

This immediately adds two new extension functions on our Factory:

fun withTraits(vararg traits: FactoryTrait)

fun withTrait(trait: FactoryTrait)

We can now start building factories with distinctive characteristics:

val californiaFactory: AddressFactory = AddressFactory()
  .withTraits(CaliforniaTrait)

Producing objects from a Factory

As described above we utilize factories in order to produce fixture data.

This can be done by invoking the following function on a Factory:

val address: Address = californiaFactory.produce()

If we need to generate more than on instance of our fixture data, we can utilize the following function of a Factory that returns a Sequence of objects:

val addresses: List<Address> = californiaFactory.produceMany()
  .take(5)
  .toList()

Generating dynamic values every time

Most of the time in our fixture data we might need to produce random values, or have a new value generated every time we invoke .produce() on one of our factories.

For that purpose, we include a typealias in our library, named Yielded and our proposed usage is the following:

class AddressFactory(
  private var city: String = "city",
  private var state: String = "state",
  private var streetNum: Yielded<Int> = { Random.nextint(1,5) }
) : Factory<Address>, TraitEnhancedFactory {

  fun withCity(city: String) = apply {
    this.city = city
  }

  fun withState(state: String) = apply {
    this.state = state
  }

  fun withStreetNum(streetNum: Int) = apply {
    this.streetNum = { streetNum }
  }

  fun withStreetNum(streetNum: Yielded<Int>) = apply {
    this.streetNum = streetNum
  }

  override fun produce() : Address = Address(
    city = city,
    state = state,
    streetNum = streetNum()
  )
}

From the above example, we can see that we have two new functions in our Factory.

These functions allow us to override the value generated for streetNum to have either a static value every time we invoke .produce(), or a dynamic one. By default, the value of it will be a lambda function which delegates to Random.nextInt() each time.

Publishing

  • Bump version in gradle.properties of kfactory module.
  • Execute the following to upload artifact:
$ ./gradlew :kfactory:publish \
            --no-daemon --no-parallel \
            -Psigning.secretKeyRingFile=<keyring_file_path> \
            -Psigning.password=<keyring_password> \
            -Psigning.keyId=<keyring_id> \
            -PmavenCentralUsername=<nexus_username> \ 
            -PmavenCentralPassword=<nexus_password>

After this operation finishes, you can promote the artifact to be releasable with:

$ ./gradlew closeAndReleaseRepository \
            -PmavenCentralUsername=<nexus_username> \
            -PmavenCentralPassword=<nexus_password>

Maintainers

The core maintainer of this project, is the Platform Team of Blueground!

You might also like...
Saga pattern implementation in Kotlin build in top of Kotlin's Coroutines.

Module Saga Website can be found here Add in build.gradle.kts repositories { mavenCentral() } dependencies { implementation("io.github.nomisr

Kotlin microservices with REST, and gRPC using BFF pattern. This repository contains backend services. Everything is dockerized and ready to
Kotlin microservices with REST, and gRPC using BFF pattern. This repository contains backend services. Everything is dockerized and ready to "Go" actually "Kotlin" :-)

Microservices Kotlin gRPC Deployed in EC2, Check it out! This repo contains microservices written in Kotlin with BFF pattern for performing CRUD opera

A sample skeleton backend app built using Spring Boot kotlin, Expedia Kotlin Graphql, Reactive Web that can be deployed to Google App Engine Flexible environmennt

spring-kotlin-gql-gae This is a sample skeleton of a backend app that was built using: Spring Boot(Kotlin) Reactive Web Sprinng Data R2DBC with MYSQL

Modular Android architecture which showcase Kotlin, MVVM, Navigation, Hilt, Coroutines, Jetpack compose, Retrofit, Unit test and Kotlin Gradle DSL.

SampleCompose Modular Android architecture which showcase Kotlin, MVVM, Navigation, Hilt, Coroutines, Jetpack compose, Retrofit, Unit test and Kotlin

Learn-kotlin - Learning more about Kotlin in various content

Kotlin study roadmap https://kotlinlang.org/docs/reference/ Getting Started Basi

Mis experimentos con Kotlin para JetBrains Academy, certificación de Kotlin donde voy resolviendo proyectos de evaluación y haciendo actividades de cada tema.
Mis experimentos con Kotlin para JetBrains Academy, certificación de Kotlin donde voy resolviendo proyectos de evaluación y haciendo actividades de cada tema.

Kotlin Academy Mis experimentos con Kotlin para JetBrains donde voy resolviendo proyectos de evaluación y haciendo actividades de cada tema. Acerca de

Repositório criado para ser utilizado pelo projeto de Kotlin Collections desenvolvido em Kotlin nas aulas feitas através da plataforma Alura.

Projeto Kotlin Collections Repositório criado para ser utilizado pelo projeto de Kotlin Collections desenvolvido em Kotlin nas aulas feitas através da

Kotlin-GraphQL-Apollo - Sencillo cliente para consumir una API GraphQL con Apollo usando Kotlin
Kotlin-GraphQL-Apollo - Sencillo cliente para consumir una API GraphQL con Apollo usando Kotlin

Kotlin GraphQL Apollo Sencillo cliente para consumir una API GraphQL con Apollo

DS-for-Kotlin - Some classic data sturctures write in kotlin for fun

DS-for-Kotlin Just write some classic data structure by kotlin during my leisure

Owner
Blueground
Transforming the experience of big city living
Blueground
AbstractFactoryDesignPatternWithKotlin - Abstract Factory Design Pattern With Kotlin

AbstractFactoryDesignPatternWithKotlin Abstract Factory Design Pattern With Kotl

Harun Kör 1 Jan 2, 2022
LiveDataCallAdapter - Live Data Call Adapter Factory

LiveDataCallAdapterFactory based on retrofit, the LiveData returned by the restf

Jay Wang 0 Feb 6, 2022
Repo: Programming problems with solutions in Kotlin to help avid Kotlin learners to get a strong hold on Kotlin programming.

Kotlin_practice_problems Repo: Programming problems with solutions in Kotlin to help avid Kotlin learners to get a strong hold on Kotlin programming.

Aman 0 Oct 14, 2021
Mocking for Kotlin/Native and Kotlin Multiplatform using the Kotlin Symbol Processing API (KSP)

Mockative Mocking for Kotlin/Native and Kotlin Multiplatform using the Kotlin Symbol Processing API (KSP). Installation Mockative uses KSP to generate

Mockative 121 Dec 26, 2022
Kotlin-oop - Repositório criado para ser utilizado pelo projeto de Kotlin OOP desenvolvido em Kotlin nas aulas feitas através da plataforma Alura.

Projeto React OOP Repositório criado para ser utilizado pelo projeto de Kotlin OOP desenvolvido em Kotlin nas aulas feitas através da plataforma Alura

Marcos Felipe 1 Jan 5, 2022
Kotlin-koans - Kotlin Koans are a series of exercises to get you familiar with the Kotlin Syntax

kotlin-koans-edu Kotlin Koans are a series of exercises to get you familiar with

null 1 Jan 11, 2022
Kotlin TodoMVC – full-stack Kotlin application demo

Kotlin full stack TodoMVC This project is an example implementation of the TodoMVC app written in Kotlin. More specifically, it's the Kotlin port of t

Gyula Voros 22 Oct 3, 2022
Integration Testing Kotlin Multiplatform Kata for Kotlin Developers. The main goal is to practice integration testing using Ktor and Ktor Client Mock

This kata is a Kotlin multiplatform version of the kata KataTODOApiClientKotlin of Karumi. We are here to practice integration testing using HTTP stub

Jorge Sánchez Fernández 29 Oct 3, 2022
Small kotlin library for persisting _single instances_ of kotlin data classes

PerSista Small library for persisting single instances of kotlin data classes. NB: PerSista uses typeOf() internally which is marked as @ExperimentalS

Eric Donovan 5 Nov 13, 2022
Kotlin Leaning Notes from Udacity Course | Kotlin Bootcamp for Programmers by Google

Kotlin Beginners Notes These are all personal notes taken from the Udacity Course (ud9011) of Kotlin Bootcamp for Programmers by Google as well as oth

Süha Tanrıverdi 34 Dec 10, 2022