scalatest - Does property based testing make you duplicate code? -
i'm trying replace old unit tests property based testing (pbt), concreteley scala
, scalatest - scalacheck
think problem more general. simplified situation , if have method want test:
def upcasereverse(s:string) = s.touppercase.reverse
normally, have written unit tests like:
assertequals("gnirts", upcasereverse("string")) assertequals("", upcasereverse("")) // ... corner cases think of
so, each test, write output expect, no problem. now, pbt, it'd :
property("strings reversed , upper-cased") { forall { (s: string) => assert ( upcasereverse(s) == ???) //this problem right here! } }
as try write test true string
inputs, find self having write logic of method again in tests. in case test :
assert ( upcasereverse(s) == s.touppercase.reverse)
that is, had write implementation in test make sure output correct. there way out of this? misunderstanding pbt, , should testing other properties instead, :
- "strings should have same length original"
- "strings should contain characters of original"
- "strings should not contain lower case characters" ...
that plausible sounds contrived , less clear. can more experience in pbt shed light here?
edit : following @eric's sources got this post, , there's example of mean (at applying categories 1 more time): test method times
in (f#
):
type dollar(amount:int) = member val amount = amount member this.add add = dollar (amount + add) member this.times multiplier = dollar (amount * multiplier) static member create amount = dollar amount
the author ends writing test goes like:
let ``create times should same times create`` start multiplier = let d0 = dollar.create start let d1 = d0.times(multiplier) let d2 = dollar.create (start * multiplier) // ones duplicates code of times! d1 = d2
so, in order test method, code of method duplicated in test. in case trivial multiplying, think extrapolates more complex cases.
this presentation gives clues kind of properties can write code without duplicating it.
in general useful think happens when compose method want test other methods on class:
size
++
reverse
touppercase
contains
for example:
upcasereverse(y) ++ upcasereverse(x) == upcasereverse(x ++ y)
then think break if implementation broken. property fail if:
- size not preserved?
- not characters uppercased?
- the string not reversed?
1. implied 3. , think property above break 3. not break 2 (if there no uppercasing @ example). can enhance it? about:
upcasereverse(y) ++ x.reverse.toupper == upcasereverse(x ++ y)
i think 1 ok don't believe me , run tests!
anyway hope idea:
- compose other methods
- see if there equalities seem hold (things "round-tripping" or "idempotency" or "model-checking" in presentation)
- check if property break when code wrong
note 1. , 2. implemented library named quickspec , 3. "mutation testing".
addendum
about edit: times
operation wrapper around *
there's not test. in more complex case might want check operation:
- has
unit
element - is associative
- is commutative
- is distributive addition
if of these properties fails, big surprise. if encode properties generic properties binary relation t x t -> t
should able reuse them in sorts of contexts (see scalaz monoid "laws").
coming uppercasereverse
example write 2 separate properties:
"uppercasereverse must uppercase string" >> forall { s: string => uppercasereverse(s).forall(_.isupper) } "uppercasereverse reverses string regardless of case" >> forall { s: string => uppercasereverse(s).tolowercase === s.reverse.tolowercase }
this doesn't duplicate code , states 2 different things can break if code wrong.
in conclusion, had same question before , felt pretty frustrated after while found more , more cases not duplicating code in properties, when starting thinking about
- combining tested function other functions (
.isupper
in first property) - comparing tested function simpler "model" of computation ("reverse regardless of case" in second property)
Comments
Post a Comment