Arrived from Scalacheck?
Welcome to Americium - learn the local language
Table of contents
- TOC
Come on in and take the weight off your feet, we’re friendly here. Like a nice refreshing glass of integrated shrinkage cordial?
If you want to speak Americium it’s expected that you’ll do so with a Scalacheck accent - here’s a guide to translation:
Arbitrary and Gen
First off, there is no Arbitrary analogue. Gen[+T] becomes Trials[+T]. Easy.
(Actually, there is something called Factory specifically for auto-derivation; that is a bit like Arbitrary. We’ll
get to that in a bit…)
Getting a Gen
This is nearly always explicit (we’ll cover auto-derivation in a bit).
Instead of using the companion object Gen, either import Trials.api or use a method defined on the Trials[T]
instance itself…
Scalacheck (Gen) |
Americium (api or Trials) |
|---|---|
Gen.choose, Gen.chooseNum, Gen.chooseChar, Gen.chooseBigInt |
api.integers, api.doubles, api.characters, api.bigInts (using the overloads that take lower and upper bounds) |
Gen.double, Gen.long |
api.doubles, api.longs (using the overloads without any parameters) |
Gen.oneOf |
api.choose or api.alternate (depending on whether you originally fed in T or Gen[T]) |
Gen.const |
api.only |
Gen.delay |
api.delay |
Gen.frequency |
api.chooseWithWeights |
Gen.failed |
api.impossible |
Gen.sequence |
api.sequences |
Gen.option |
<trials instance>.options |
Gen.either |
<trials instance>.or |
Gen.stringOf |
<character trials instance>.several (calling several on a character trials will build string trials by default) |
Gen.stringOfN |
<character trials instance>.lotsOfSize (calling lotsOfSize on a character trials will build string trials by default) |
Gen.listOf |
<trials instance>.lists |
Gen.listOfN |
<trials instance>.listsOfSize |
Gen.containerOf |
<trials instance>.several |
Gen.containerOfN |
<trials instance>.lotsOfSize |
NOTE: the methods in TrialsApi often have names in the plural - so Gen.double becomes api.doubles,
Gen.sequence becomes api.sequences and so on.
Gen as a monad
Trials is a monad too, with a stack-safe implementation. It has a typeclass instance for Cats’ Monad. So you can
map, flatMap, filter and mapFilter till you drop. There is even withFilter so you can enjoy the full-fat
for-comprehension flavour.
Plugging in test lambdas
Throw away Prop.forAll and call <trials instance>.withLimit(<limit>).supplyTo instead.
If you have several Gen instances, then gang together the corresponding trials with .and:
(<trials one> and <trials two> and <trials three>).withLimit(<limit>).supplyTo.
Test.Parameters
Substitute for Test.Parameters.withMinSuccessfulTests and Test.Parameters.withMaxDiscardRatio with
<trials instance>.withStrategy(_ => CasesLimitStrategy.counted(<maximumNumberOfCases>, <maximumStarvationRatio>)).
Substitute for Test.Parameters.withInitialSeed with <supply-to syntax>.withSeed, where <supplyToSyntax> is
<trials instance>.withLimit(<limit>) etc.
Substitute for Test.Parameters.withMaxSize with <supply-to syntax>.withComplexityLimit, where <supplyToSyntax> is
<trials instance>.withLimit(<limit>) etc.
Sized generators
Substitute Gen.size with api.complexities. This will reveal the appropriate complexity to your own code that builds
up trials. Typically this is done in a flat-map:
api.complexities.flatMap(complexity => <trials expression using the complexity>).
Custom Shrink instances
Delete them and dance a jig!
Scalacheck-Shapeless
Your auto-derivation needs are met by com.sageserpent.americium.Factory, which uses the marvellous Magnolia library
under the hood.
If you have a case class hierarchy rooted at Root and you want Trials<Root>, then pull in an implicitly derived
instance via:
Scala 2.13
implicitly[Factory[Root]].trials
Scala 3
given evidence: Factory[Root] = Factory.autoDerived // May need this if `Root` has a recursive definition like, say, `List[T]`.
implicitly[Factory[Root]].trials
As with Scalacheck-Shapeless, you will probably need to supply a few of your own implicit definitions to bootstrap the
auto-derivation, it’s the same drill only this time it is evidences for various Factory[T] instantiations that you
need to supply.
As a guide, here are the built-in implicit definitions for Scala 2.13 and Scala 3.
Next topic: JUnit5 again…