Arbitrater is a Kotlin library for creating arbitrary instances of classes by reflection for use in testing

Overview

Arbitrater

Download Build Status License

Arbitrater is a library for creating arbitrary instances of classes by reflection, for example to use in testing. It was designed primarily for use with Kotlin 'data classes', but may work with regular classes too.

It is similar to libraries like random-beans and podam, but with support for nullable types and default values.

Quick start

data class Order(val name: String, val quantity: Int = 1, val promotionCode: String?)

val defaultOrder: Order = arbitrary()
println(defaultOrder) // Order(description=\VccV/esz{54[!FAU(a{, quantity=1, promotionCode=5b$`i1AsT54t[Hwf%W*&)    

val customOrder = Order::class.arbitrater()
        .generateNulls()
        .useDefaultValues(false)
        .createInstance()    
        
println(customOrder) // Order(description= 5{L_vUnvMIJ!y]03*
   

Features

Default generators

Out of the box, Arbitrater can generate values for:

  • Byte, Short, Int, Long
  • Double, Float
  • BigInteger, BigDecimal
  • String
  • UUID
  • LocalDate, LocalDateTime, Instant
  • Enums
  • Lists of any supported type
  • Sets of any supported type
  • Collections of any supported type (generates a non-mutable list)
  • Maps of any supported type
  • Classes with a primary constructor that consist of any supported type
  • Nested classes that meet the above requirements.

Customising and extending

The options below can be applied either per-instance, or globally.

To customise per-instance, call 'arbitrater()', which provides a fluent API for configuration.

To customise globally, either use the DefaultConfiguration object or provide a bootstrap configuration. (See the next section)

Customising generated instances

Currently the following properties can be configured:

  • Whether nullable types have values generated for them, or are left as null
  • Whether optional parameters have values generated for them, or are left as the default value.

Extending supported types

If you need to generate a type that does not work out of the box, or you want to customise the way a type is generated, call registerGenerator. It takes a lambda or method reference with no parameters that returns a single value.

If multiple generators are registered for the same type, the last one wins.

Note: If the return type of the function is nullable, it is projected as a non-nullable type. This is to ease interoperability with Java.

Bootstrap configuration

It is possible to create a bootstrap configuration file that will configure Arbitrater for your library / project automatically. This feature makes use of the ServiceLoader mechanism provided by core Java.

  1. Implement the interface ArbitraterInitializer
    • Set a priority. If there are multiple bootstrap configurations with overlapping settings, the highest priority wins.
    • In the method bootstrapConfiguration(), register any generators you require. You can also change the default settings.
  2. Create the directory META-INF/services
  3. Add a file called 'com.tyro.oss.arbitrater.ArbitraterInitializer' to this directory
  4. Add a single line to the file with the 'binary name' of your implementing class. In many cases this will be the fully qualified class name.
  5. That's it - your code will be executed once, the first time an arbitrary instance is generated.

Note: You can replace steps 2-5 with the AutoService library

Limitations

  • Currently only classes with a primary constructor are supported - this means 'POJOS' defined in Java libraries may not work
  • Arrays are not supported. This is because the primary use case was data-classes, and arrays do not play nicely with data classes.
  • Error handling is not as informative as it could be
Comments
  • BUG: useDefaultValues overrides withValue

    BUG: useDefaultValues overrides withValue

    arbitrater().useDefaultValues(false).withValue("id", "myid") produces a different result to arbitrater().withValue("id", "myid").useDefaultValues(false). In the latter, the id value will not be set to myid since the useDefaultValues doesn't copy over the specificValues Map. I think that this goes against the builder-like nature of the API since order of the invocations matter making the API confusing.

    opened by nikimicallef 5
  • registerGenerator doesn't work since Kotlin 1.4

    registerGenerator doesn't work since Kotlin 1.4

    The following code in registerGenerator() will always return Any().

    val returnType = generator.reflect()!!.returnType.withNullability(false)
    

    Works ok in Kotlin 1.3, but not from Kotlin 1.4 onwards

    opened by thecodinganalyst 3
  • Two bugs related to Issue 8

    Two bugs related to Issue 8

    Fixes https://github.com/tyro/arbitrater/issues/8

    First issue from @nikimicallef was because of return this inside withValue(). It mutated the class in place, and specificValues map was lost if further config calls were made. I guess either approach would work but can't mix and match, so fixed by making withValue() also return a copy.

    Second issue - specific values are lost of they are defaulted and useDefaultsValues is true was a logic bug, resolved by checking to see if the constructor param is defined and not throwing it away if so.

    I guess it should be noted these fixes between them change behaviour that some unfortunate tests may have been accidentally relying on? When released probably worth noting...

    opened by sgerber-prospection 2
  • Fix registerGenerator not working with kotlin 1.4 and above

    Fix registerGenerator not working with kotlin 1.4 and above

    Should fix https://github.com/tyro/arbitrater/issues/9

    Seems reflect() changed in Kotlin 1.4 and above such that it picked up the Any from fun registerGenerator(generator: () -> Any). Making this a generic function appears to have worked! Even without marking it as reified, the type came through.

    Also noticed while I was in there that reflect() started returning null when Java methods were passed in, like randomBoolean. Fixed that too by discovering it was being transformed into a CallableReference, and we could get the return type off it through reflection.

    Phew!

    opened by sgerber-prospection 1
  • Cannot access settings when extending ArbitraterInitializer

    Cannot access settings when extending ArbitraterInitializer

    We are trying to register default settings using the ServiceLoader mechanism as described in the README.

    What we want to do is set the default/null generator but cannot access the settings object. Are we missing something or is there something missing from the docs?

    btw - thanks for a great lib.

    opened by tddmonkey 0
Owner
Tyro. Better business banking.
Tyro. Better business banking.
DSL for JPA Criteria API without generated metamodel and reflection.

Kotlin JDSL Kotlin JDSL is DSL for JPA Criteria API without generated metamodel and reflection. It helps you write a JPA query like writing an SQL sta

LINE 379 Jan 7, 2023
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 Symbol Processor to auto-generate extensive sealed classes and interfaces for Android and Kotlin.

SealedX ?? Kotlin Symbol Processor to auto-generate extensive sealed classes and interfaces for Android and Kotlin. Why SealedX? SealedX generates ext

Jaewoong Eum 236 Nov 30, 2022
An AutoValue extension that generates binary and source compatible equivalent Kotlin data classes of AutoValue models.

AutoValue Kotlin auto-value-kotlin (AVK) is an AutoValue extension that generates binary-and-source-compatible, equivalent Kotlin data classes. This i

Slack 19 Aug 5, 2022
Clean MVVM with eliminating the usage of context from view models by introducing hilt for DI and sealed classes for displaying Errors in views using shared flows (one time event), and Stateflow for data

Clean ViewModel with Sealed Classes Following are the purposes of this repo Showing how you can remove the need of context in ViewModels. I. By using

Kashif Mehmood 22 Oct 26, 2022
Kbackend - A simple backend library for creating backends in Kotlin/Java

kbackend A simple backend library for creating backends in Kotlin/Java Setup Thi

Niclas 3 Feb 2, 2022
To-do-List - Creating a Reminders and Tasks app with Kotlin

To do List ?? App de Lembretes e Tarefas com Kotlin Telas do App Tecnologias Kot

Alini Rodrigues Ferreira 4 May 18, 2022
An idiomatic Kotlin DSL for creating regular expressions.

Ketex An idiomatic Kotlin DSL for creating regular expressions. For documentation and usage instructions, please take a look at the docs. Here's the m

TheOnlyTails 24 Nov 8, 2022
Maxibon kata for Kotlin Developers. The main goal is to practice property based testing.

Kata Maxibon for Kotlin. We are here to practice property based testing. We are going to use KotlinTest to write our tests. We are going to practice p

Karumi 44 Oct 3, 2022
TODO API Client Kata for Kotlin Developers. The main goal is to practice integration testing using MockWebServer

KataTODOApiClient for Kotlin We are here to practice integration testsing using HTTP stubbing. We are going to use MockWebServer to simulate a HTTP se

Karumi 61 Nov 20, 2022
Screenshot Kata for Android Developers with Kotlin. The main goal is to practice UI Screenshot Testing.

KataScreenshot in Kotlin We are here to practice UI testing using screenshot tests for Android. We are going to use Espresso to interact with the Appl

Karumi 76 Nov 20, 2022
Super Heroes Kata for Android Developers in Kotlin. The main goal is to practice UI Testing.

KataSuperHeroes in Kotlin We are here to practice UI Testing. We are going to use Espresso to interact with the Application UI. We are going to use Ko

Karumi 86 Nov 20, 2022
Kotlin Unit Testing Examples

Kotlin Unit Testing Examples Table of Contents Application Gradle, Kotlin & Groovy Junit4 Junit5 KotlinTest Spek Mockito Mockito-Kotlin Mockk Strikt T

Jarosław 110 Dec 11, 2022
Nice and simple DSL for Espresso Compose UI testing in Kotlin

Kakao Compose Nice and simple DSL for Espresso Compose in Kotlin Benefits Readability Reusability Extensible DSL How to use it Create Screen Create yo

null 74 Dec 26, 2022
Code for the Advanced Android Kotlin Testing Codelab 5.1-5.3

TO-DO Notes - Code for 5.1-5.3 Testing Codelab Code for the Advanced Android Kotlin Testing Codelab 5.1-5.3 Introduction TO-DO Notes is an app where y

Jorge M 1 Jun 7, 2022
Ktor is an asynchronous framework for creating microservices, web applications and more.

ktor-sample Ktor is an asynchronous framework for creating microservices, web applications and more. Written in Kotlin from the ground up. Application

mohamed tamer 5 Jan 22, 2022
A study into creating a fully automatic FRC robot

AutoFRC This is a study into creating a fully automatic FRC robot. This process relies on several key algorithms: pose estiation: using the WpiLib Dif

null 1 Jun 29, 2022
A seed and demo about how to do end-to-end testing of a Dataflow pipeline

dataflow-e2e-demo This is a demo and a seed project to show how you can end-to-end test a Dataflow pipeline. You can find more about by follwing this

null 0 Dec 18, 2021