# Testing with MUnit and ScalaTest — Scala

Source: https://www.geekswithgeeks.com/en/scala/e-testing

> Write unit and property-based tests for Scala code.

## Testing Scala code

Popular test frameworks include **MUnit** (simple, fast, with clear diffs, used in this course's examples), **ScalaTest** (many styles such as `AnyFunSuite`, `AnyFlatSpec` and `AnyWordSpec`, with rich matchers like `result shouldBe 42`) and **weaver** or **munit-cats-effect** for effectful code. **Property-based testing** with **ScalaCheck** generates many random inputs and checks that properties always hold, such as "sorting twice equals sorting once" or "encode then decode returns the original", and it shrinks failing inputs to minimal examples. Pure functions and ADTs make tests straightforward: no mocks are needed when logic does not perform I/O. For I/O boundaries, depend on **traits** and pass test implementations, or use test containers (**testcontainers-scala**) for databases and message brokers. Test `Future` and `IO` code with framework support for asynchronous tests rather than blocking. Run tests with `sbt test`, or `scala test .` with Scala CLI, and include coverage with **scoverage** where it is useful.

## Example-based and property-based tests

MUnit suites with a ScalaCheck property.

```scala
// src/test/scala/PricingSuite.scala
import munit.ScalaCheckSuite
import org.scalacheck.Prop.forAll
import org.scalacheck.Gen

object Pricing:
  def withGstPaise(paise: Long): Long = paise + (paise * 18 + 50) / 100   // rounds to nearest paisa
  def discountPaise(paise: Long, percent: Int): Long =
    require(percent >= 0 && percent <= 100)
    paise * (100 - percent) / 100

class PricingSuite extends ScalaCheckSuite:
  test("adds 18% GST") {
    assertEquals(Pricing.withGstPaise(10000), 11800L)
  }

  test("rejects invalid discounts") {
    intercept[IllegalArgumentException](Pricing.discountPaise(1000, 120))
  }

  val amounts = Gen.choose(0L, 10_000_000L)
  val percents = Gen.choose(0, 100)

  property("discount never increases the price") {
    forAll(amounts, percents) { (paise, pct) =>
      Pricing.discountPaise(paise, pct) <= paise
    }
  }

  property("GST is never negative and never more than 18% plus rounding") {
    forAll(amounts) { paise =>
      val extra = Pricing.withGstPaise(paise) - paise
      extra >= 0 && extra <= paise * 18 / 100 + 1
    }
  }

// build.sbt: libraryDependencies += "org.scalameta" %% "munit-scalacheck" % "1.1.0" % Test
```

## A thousand monkeys testing your rules

Example tests check the cases you thought of. Property tests send a crowd of random shoppers through your checkout, and when one of them breaks it, ScalaCheck finds the simplest version of that shopper to show you.

**Quiz:** What does property-based testing with ScalaCheck do?

- [ ] Tests only one hard-coded input
- [ ] Measures performance
- [ ] Formats code
- [x] Generates many random inputs to check that a property always holds, shrinking failures to minimal examples

*Answer:* Generates many random inputs to check that a property always holds, shrinking failures to minimal examples. ScalaCheck generates inputs and shrinks counterexamples.
