# Avoiding check mate when testing <!-- Put the link to this slide here so people can follow --> slide: https://hackmd.io/@thanh/S1I8Qr-G6 --- ## Introduction - Thanh Le - Software Engineer at Recorded Future - Developer at Lichess.org --- ### Lichess - Second largest chess online in the world - Probably the biggest open source server in the wolrd - Non profit organization - Most of contributors (dev and non dev) are volunteers --- ### Scenario - You have to work with an evil colleague to implement the addition function - The evil task is writing implement - Your task is writing tests - How do you make sure the implmentation is correct. ```scala def add(a: Int, b: Int): Int ``` --- ### Unit Test ![](https://hackmd.io/_uploads/HJkj8BbG6.jpg) --- ### Unit Test - Example based testing - Choosing input/output - False sense of security - Cannot cover all cases - Hard to invole - Hard to read --- ### Property Based Test (PBT) - Define properties of your functions - Write your tests to verify those properties --- ### Advantages of PBT - It helps us understand more about our functions - It can generate many more examples than we would likely have done manually - It makes tests significantly cleaner and more robust - It makes fewer implicit assumptions about fixture data --- ### 1st example Propeties for addtion function: - Commutative Property: `a + b == b + a - Associative Property: `(a + b) + c == a + (b + c)` - Identity Property: `a + 0 == 0 + a == a` - Distributive property: `(a + b) * c = a*c + b*c` --- ### Chess Bitboards Bitboards (or better Square Set), are in essence, finite sets of up to 64 elements - all the squares of a chessboard, one bit per square. ```scala opaque type Bitboard = Long object Bitboard: extension (a: Bitboard) def isEmpty: Boolean def nonEmpty: Boolean def contains(square: Square): Boolean def add(square: Square): Bitboard def remove(square: Square): Bitboard def intersects(o: Bitboard): Boolean def isDisjoint(o: Bitboard): Boolean def squares: List[Square] ``` --- ### Bitboards Testing ```scala test("the result of add should contain the added square"): forAll: (bb: Bitboard, square: Square) => bb.add(square).contains(square) == true ``` --- ### Bitboards Testing ```scala test("intersects should be true when the two bitboards have at least one common square"): forAll: (b1: Bitboard, b2: Bitboard, p: Square) => b1.add(p).intersects(b2.add(p)) test("isDisjoint should be false when the two bitboards have at least common square"): forAll: (b1: Bitboard, b2: Bitboard, p: Square) => b1.add(p).isDisjoint(b2.add(p)) == false test("isDisjoint and intersects always return the opposite value"): forAll: (b1: Bitboard, b2: Bitboard) => b1.isDisjoint(b2) != b1.intersects(b2) ``` --- ### A Service The requirement is getting data from `Remote`, and compare with data from `Local` and return the different if there is any and save the latest data to Local. ```scala trait DiffService[F[_]]: def diff: F[Option[DiffResult]] object DiffService: def apply[F[_]](local: Local[F], remote: Remote[F]): DiffService trait Remote[F[_]]: def fetch: F[Data] trait Local[F[_]]: def save(data: Data): F[Unit] def fetch: F[Option[Data]] ``` --- ### Preparation ```scala case class TestEnv(local: Option[Data], remote: Data) type Test[A] = State[TestEnv, A] val remote: Remote[Test] = new Remote[Test]: State.inspect(_.remote) val local: Local[Test] = new Local[Test]: override def fetch: Test[Option[Data]] = State.inspect(_.local) override def save(data: Data): Test[Unit] = State.modify(_.copy(local = data.some)) private val diffService = DiffService.instance(local, remote) ``` --- ### Tests ```scala test("save the latest data"): forAll: env: TestEnv => diffService.diff.runS(env).value.local == env.remote.some test("compare only if there is previous data"): forAll: env: TestEnv => diffService.diff.runS(env).value.isDefined == env.local.isDefined test("return correct result"): forAll: env: TestEnv => diffService.diff.runA(env).value == env.local.map(Diff.compare(_, env.remote)) ``` --- ### Thank you
{"description":"View the slide with \"Slide Mode\".","title":"Testing","contributors":"[{\"id\":\"28f9d763-8570-42fd-946f-90c08e4ae219\",\"add\":6680,\"del\":2195}]"}
    131 views