rchain / rchain

Blockchain (smart contract) platform using CBC-Casper proof of stake + Rholang for concurrent execution.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Introduce support for effectful testing

tgrospic opened this issue · comments

Overview

Currently, we don't have support for F[_] effectful computation in tests so tests are using _.runSyncUnsafe() to get the value and assert result. This is not desired, recommended, runs slower and also more more difficult to read and write.

Solution

Introduce libraries to extend Scalatest and scalacheck to support working with F[_] values without hacks.
https://typelevel.org/cats-effect/docs/2.x/guides/testing

Two libraries that we need are:

  1. cats-effect-testing which enables body of tests to be effect type like IO or Task
    https://github.com/typelevel/cats-effect-testing#scalatest
  2. ScalaCheck Effect which enables property testing with effectful values forAllF
    https://github.com/typelevel/scalacheck-effect#scalacheck-effect
  • Upgrade Scalatest, scalacheck and scalactic to latest versions to support cats-effect-testing which can only works with Scalatest 3.1.0
    https://github.com/typelevel/cats-effect-testing/blob/v0.3.0/build.sbt#L56
  • Introduce monix-testing-scalatest library to support minix Task in tests
    https://github.com/monix/monix-testing
  • Introduce cats-effect-testing and scalacheck-effect into the project and improve InMemoryKeyValueStoreSpec and LmdbKeyValueStoreSpec by using effectful version. This means removal of _.runSyncUnsafe() by using forAllF instead of forAll together with using Prop instead of assert or should (e.g. shouldBe is the same as ?=).
    • examples of property based tests can be found here (for now only on feature/v0.12 branch)
      "block hash" should "not match for changed block hash" in {
      implicit val aBlockHash = arbitraryBlockHash
      check {
      forAll { (block: BlockMessage, genBlockHash: ByteString) =>
      val isModified = genBlockHash != block.blockHash
      val hash = block.blockHash
      val blockModified = block.copy(blockHash = genBlockHash)
      val hashModified = ProtoUtil.hashBlock(blockModified)
      // Check hashes are different for modified block
      hashModified !== hash ?= isModified
      }
      }
      }
      "block hash" should "not match for changed sequence number" in {
      check {
      forAll { (block: BlockMessage, genSeqNum: Int) =>
      val isModified = genSeqNum != block.seqNum
      val hash = ProtoUtil.hashBlock(block)
      val blockModified = block.copy(seqNum = genSeqNum)
      val hashModified = ProtoUtil.hashBlock(blockModified)
      // Check hashes are different for modified block
      hashModified !== hash ?= isModified
      }
      }
      }
      "block hash" should "not match for changed block number" in {
      check {
      forAll { (block: BlockMessage, genBlockNumber: Long) =>
      val isModified = genBlockNumber != block.body.state.blockNumber
      val hash = ProtoUtil.hashBlock(block)
      val blockModified = block.copy(
      body = block.body.copy(state = block.body.state.copy(blockNumber = genBlockNumber))
      )
      val hashModified = ProtoUtil.hashBlock(blockModified)
      // Check hashes are different for modified block
      hashModified !== hash ?= isModified
      }
      }
      }
      "block hash" should "not match for changed justifications" in {
      implicit val aJustification = arbitraryJustification
      check {
      forAll { (block: BlockMessage, genJustifications: List[Justification]) =>
      val isModified = genJustifications != block.justifications
      val hash = ProtoUtil.hashBlock(block)
      val blockModified = block.copy(justifications = genJustifications)
      val hashModified = ProtoUtil.hashBlock(blockModified)
      // Check hashes are different for modified block
      hashModified !== hash ?= isModified
      }
      }
      }
  • Add WebApi tests as property based tests like for KeyValueStore. Generate arbitrary input and output data and assert ib underlying BlockApi received right input and returned right output.