Some checks failed
/ Build and Test Library Snapshot (pull_request) Failing after 1m1s
154 lines
3.6 KiB
Markdown
154 lines
3.6 KiB
Markdown
# gs-datagen
|
|
|
|
[GS Open Source](https://garrity.co/oss.html) |
|
|
[License (MIT)](./LICENSE)
|
|
|
|
Test data generation library for Scala 3. `gs-datagen` provides _composable_
|
|
generator definitions that can be reused across tests.
|
|
|
|
- [Usage](#usage)
|
|
- [Dependency](#dependency)
|
|
- [Imports](#imports)
|
|
- [Examples](#examples)
|
|
- [Example: Generate a Random User](#example-generate-a-random-user)
|
|
- [Example: Require Input](#example-require-input)
|
|
- [Supported Generators](#supported-generators)
|
|
- [Donate](#donate)
|
|
|
|
## Usage
|
|
|
|
### Dependency
|
|
|
|
```
|
|
object Gs {
|
|
val Datagen: ModuleID =
|
|
"gs" %% "gs-datagen-core-v0" % "$VERSION"
|
|
}
|
|
```
|
|
|
|
### Imports
|
|
|
|
The standard way to use `gs-datagen` is to import the entire package, which
|
|
pulls in `Gen[A]`, `Generated[A]` and `Datagen[A, -I]`:
|
|
|
|
```scala
|
|
import gs.datagen.v0._
|
|
```
|
|
|
|
## Examples
|
|
|
|
For the following examples, the following types (abbreviated) are relevant:
|
|
|
|
```scala
|
|
opaque type Name = String
|
|
opaque type DateOfBirth = LocalDate
|
|
opaque type Karma = Long
|
|
|
|
enum Role:
|
|
case Regular, Mod, Admin
|
|
|
|
case class User(
|
|
name: Name,
|
|
dateOfBirth: DateOfBirth,
|
|
karma: Karma,
|
|
role: Role
|
|
)
|
|
```
|
|
|
|
### Example: Generate a Random User
|
|
|
|
One way to go about this is to define generators for each type and then compose
|
|
those generators for `User`:
|
|
|
|
```scala
|
|
import gs.datagen.v0._
|
|
import gs.datagen.v0.generators.MinMax
|
|
|
|
val nameGen: Gen[Name] =
|
|
Gen.string.alpha(4, 16).map(Name(_))
|
|
|
|
val dateOfBirthGen: Gen[LocalDate] =
|
|
Gen.date.beforeToday(
|
|
days = MinMax.Zero,
|
|
months = MinMax.nonNegative(0, 11),
|
|
years = MinMax.nonNegative(18, 80)
|
|
).map(DateOfBirth(_))
|
|
|
|
val karmaGen: Gen[Karma] =
|
|
Gen.long.inRange(-100L, 100L).map(Karma(_))
|
|
|
|
val roleGen: Gen[Role] =
|
|
Gen.oneOf.fixedChoices(Role.Regular, Role.Mod, Role.Admin)
|
|
```
|
|
|
|
and the composition:
|
|
|
|
```scala
|
|
val userGen: Gen[User] =
|
|
for
|
|
name <- nameGen
|
|
dateOfBirth <- dateOfBirthGen
|
|
karma <- karmaGen
|
|
role <- roleGen
|
|
yield User(name, dateOfBirth, karma, role)
|
|
```
|
|
|
|
Generators may also be defined inline if separate definitions are not useful:
|
|
|
|
```scala
|
|
val userGen: Gen[User] =
|
|
for
|
|
name <- Gen.string.alpha(4, 16).map(Name(_))
|
|
dateOfBirth <- dateOfBirthGen
|
|
karma <- Gen.long.inRange(-100L, 100L).map(Karma(_))
|
|
role <- roleGen
|
|
yield User(name, dateOfBirth, karma, role)
|
|
```
|
|
|
|
Once that generator exists, users may be generated:
|
|
|
|
```scala
|
|
val user: User = userGen.gen()
|
|
```
|
|
|
|
The `Generated` type class can be used as well:
|
|
|
|
```scala
|
|
given Generated[User] = Generated.of(userGen)
|
|
|
|
val user2: User = Generated[User].generate()
|
|
```
|
|
|
|
### Example: Require Input
|
|
|
|
What if we want a way to generate users randomly, but always require the caller
|
|
to specify a user role? This can be accomplished by requiring input when
|
|
generating data:
|
|
|
|
```scala
|
|
val roleUserGen: Datagen[User, Role] =
|
|
for
|
|
name <- Gen.string.alpha(4, 16).map(Name(_))
|
|
dateOfBirth <- dateOfBirthGen
|
|
karma <- Gen.long.inRange(-100L, 100L).map(Karma(_))
|
|
yield (role: Role) => User(name, dateOfBirth, karma, role)
|
|
```
|
|
|
|
The usage of this type is different and does not support `Generated`:
|
|
|
|
```scala
|
|
val admin: User = roleUserGen.generate(Role.Admin)
|
|
val mod: User = roleUserGen.generate(Role.Mod)
|
|
val regular: User = roleUserGen.generate(Role.Regular)
|
|
```
|
|
|
|
## Supported Generators
|
|
|
|
For a complete list of generators supported out of the box, please refer to the
|
|
[Gen](modules/core/src/main/scala/gs/datagen/v0/gen.scala) definition,
|
|
which enumerates and documents all options.
|
|
|
|
## Donate
|
|
|
|
Enjoy this project or want to help me achieve my [goals](https://garrity.co)?
|
|
Consider [Donating to Pat on Ko-fi](https://ko-fi.com/gspfm).
|