zio-bdd

A BDD testing framework integrating Gherkin-style tests with ZIO’s effect system

View the Project on GitHub EtaCassiopeia/zio-bdd

Getting Started with zio-bdd

This guide will help you set up and run your first test using zio-bdd, a Behavior-Driven Development (BDD) testing framework for Scala 3 and ZIO. By the end, you’ll have a simple test suite up and running, demonstrating the core concepts of zio-bdd.

Prerequisites

Step 1: Add Dependencies

In your build.sbt, add the following dependencies:

libraryDependencies += "io.github.etacassiopeia" %% "zio-bdd" % "0.1.0" % Test,
libraryDependencies += "dev.zio" %% "zio" % "2.1.17" % Test,
libraryDependencies += "dev.zio" %% "zio-test" % "2.1.17" % Test,

Then, enable the zio-bdd test framework:

Test / testFrameworks += new TestFramework("zio.bdd.ZIOBDDFramework")

Step 2: Create a Feature File

Feature files describe the behavior of your application in Gherkin syntax. Create a directory for your feature files, e.g., src/test/resources/features, and add a file named greeting.feature:

Feature: User Greeting
  Scenario: Greet a user
    Given a user named "Alice"
    When the user is greeted
    Then the greeting should be "Hello, Alice!"

This feature file defines a simple scenario where a user is greeted by name.

Step 3: Define Step Definitions

Step definitions connect the Gherkin steps to executable Scala code. Create a Scala file, e.g., GreetingSpec.scala, in your test directory:

package zio.bdd.example

import zio.*
import zio.bdd.core.{Assertions, Suite}
import zio.bdd.core.step.ZIOSteps
import zio.schema.{DeriveSchema, Schema}

case class Context(userName: String, greeting: String)
object Context {
  implicit val schema: Schema[Context] = DeriveSchema.gen[Context]
}

@Suite(
  featureDir = "src/test/resources/features",
  reporters = Array("pretty"),
  parallelism = 1
)
object GreetingSpec extends ZIOSteps[Any, Context] {
  Given("a user named " / string) { (name: String) =>
    ScenarioContext.update(_.copy(userName = name))
  }

  When("the user is greeted") {
    for {
      ctx <- ScenarioContext.get
      _   <- ScenarioContext.update(_.copy(greeting = s"Hello, ${ctx.userName}!"))
    } yield ()
  }

  Then("the greeting should be " / string) { (expectedGreeting: String) =>
    ScenarioContext.get.map(_.greeting).map { actualGreeting =>
      Assertions.assertTrue(actualGreeting == expectedGreeting, s"Expected '$expectedGreeting', got '$actualGreeting'")
    }
  }
}

Explanation

Step 4: Run the Tests

To run the tests, use SBT:

sbt test

You should see output indicating that the scenario passed:

[info] Feature: User Greeting
[info]   Scenario: Greet a user
[info]     Given a user named "Alice"                                  [PASSED]
[info]     When the user is greeted                                    [PASSED]
[info]     Then the greeting should be "Hello, Alice!"                 [PASSED]

Next Steps