or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

core-specifications.mddsl-components.mdindex.mdintegration-features.mdmatcher-system.mdmutable-specifications.mdreporting.mdtest-execution.md
tile.json

integration-features.mddocs/

Integration Features

Specs2 provides comprehensive integration modules for popular testing frameworks and tools including JUnit, ScalaCheck for property-based testing, Mockito for mocking, and analysis tools for code dependencies.

JUnit Integration

SpecificationWithJUnit

JUnit-compatible specification class that can be run by JUnit runners.

@RunWith(classOf[JUnitRunner])
abstract class SpecificationWithJUnit extends Specification

Usage Example:

import org.specs2._
import org.specs2.runner.JUnitRunner
import org.junit.runner.RunWith

@RunWith(classOf[JUnitRunner])
class CalculatorJUnitSpec extends SpecificationWithJUnit { def is = s2"""
  Calculator specification for JUnit
    add two numbers    $add
    subtract numbers   $subtract
  """
  
  def add = Calculator.add(2, 3) must beEqualTo(5)
  def subtract = Calculator.subtract(5, 2) must beEqualTo(3)
}

SpecWithJUnit

Lightweight JUnit-compatible specification.

@RunWith(classOf[JUnitRunner])
abstract class SpecWithJUnit extends Spec

Usage Example:

@RunWith(classOf[JUnitRunner])
class SimpleJUnitSpec extends SpecWithJUnit { def is = s2"""
  Simple JUnit specification
    basic test    ${ 1 + 1 must beEqualTo(2) }
  """
}

JUnitRunner

JUnit test runner implementation for executing specs2 specifications.

class JUnitRunner(klass: Class[_]) extends Runner {
  def run(notifier: RunNotifier): Unit
  def getDescription: Description
  def testCount(): Int
}

IDE Integration:

  • IntelliJ IDEA: Automatically recognizes @RunWith(classOf[JUnitRunner]) specifications
  • Eclipse: Supports JUnit runner integration
  • NetBeans: Compatible with JUnit test execution

Maven Integration:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-surefire-plugin</artifactId>
  <version>2.22.2</version>
  <configuration>
    <includes>
      <include>**/*Spec.java</include>
      <include>**/*Test.java</include>
    </includes>
  </configuration>
</plugin>

ScalaCheck Integration

ScalaCheckProperty

Integration trait for property-based testing with ScalaCheck.

trait ScalaCheckProperty {
  def prop[T: Arbitrary](f: T => Boolean): Prop
  def prop[T1: Arbitrary, T2: Arbitrary](f: (T1, T2) => Boolean): Prop
  def prop[T1: Arbitrary, T2: Arbitrary, T3: Arbitrary](f: (T1, T2, T3) => Boolean): Prop
  def forAll[T: Arbitrary](f: T => Boolean): Prop
  def forAll[T: Arbitrary](gen: Gen[T])(f: T => Boolean): Prop
}

Usage Example:

import org.specs2._
import org.specs2.scalacheck.ScalaCheckProperty
import org.scalacheck.{Arbitrary, Gen}

class PropertySpec extends Specification with ScalaCheckProperty { def is = s2"""
  String properties
    concatenation is associative    $concatenationAssoc
    reverse twice is identity       $reverseTwice
    length is preserved             $lengthPreserved
  """
  
  def concatenationAssoc = prop { (s1: String, s2: String, s3: String) =>
    (s1 + s2) + s3 must beEqualTo(s1 + (s2 + s3))
  }
  
  def reverseTwice = prop { (s: String) =>
    s.reverse.reverse must beEqualTo(s)
  }
  
  def lengthPreserved = prop { (s: String) =>
    s.reverse.length must beEqualTo(s.length)
  }
}

ScalaCheckParameters

Configuration parameters for ScalaCheck property testing.

case class ScalaCheckParameters(
  minTestsOk: Int = 100,
  maxDiscardRatio: Float = 5.0f,
  minSize: Int = 0,
  maxSize: Int = 100,
  rng: java.util.Random = new java.util.Random,
  workers: Int = 1,
  testCallback: TestCallback = new TestCallback {},
  maxDiscarded: Int = 500,
  customClassLoader: Option[ClassLoader] = None
)

Custom Parameters:

class CustomPropertySpec extends Specification with ScalaCheckProperty { def is = s2"""
  Custom property testing
    large dataset property    $largeDataset
  """
  
  implicit val params = ScalaCheckParameters(
    minTestsOk = 1000,      // Run 1000 tests instead of default 100
    maxSize = 10000,        // Generate larger test data
    workers = 4             // Use 4 parallel workers
  )
  
  def largeDataset = prop { (list: List[Int]) =>
    list.sorted.reverse must beEqualTo(list.sortWith(_ > _))
  }
}

ScalaCheckPropertyCreation

Methods for creating properties from functions.

trait ScalaCheckPropertyCreation {
  def property[T: Arbitrary](f: T => Prop): Property
  def property[T: Arbitrary](name: String)(f: T => Prop): Property
  def propertyWithSeed[T: Arbitrary](seed: Long)(f: T => Prop): Property
}

ScalaCheckPropertyDsl

DSL for convenient property definition.

trait ScalaCheckPropertyDsl {
  def check[T: Arbitrary](f: T => Boolean): Fragment
  def checkAll[T: Arbitrary](f: T => Boolean): Fragment
  def verify[T: Arbitrary](f: T => Prop): Fragment
}

DSL Usage:

class PropertyDslSpec extends Specification with ScalaCheckPropertyDsl { def is = s2"""
  Property DSL examples
    list concatenation    ${check { (l1: List[Int], l2: List[Int]) =>
      (l1 ++ l2).length == l1.length + l2.length
    }}
    
    string operations     ${verify { (s: String) =>
      (s.length >= 0) :| "length non-negative" &&
      (s.reverse.reverse == s) :| "reverse is involution"
    }}
  """
}

AsResultProp

Converting ScalaCheck properties to specs2 results.

trait AsResultProp {
  implicit def propAsResult(prop: Prop): AsResult[Prop] 
  implicit def propertyAsResult(property: Property): AsResult[Property]
  implicit def genAsResult[T](gen: Gen[T]): AsResult[Gen[T]]
}

Parameters and Configuration

Fine-grained control over property testing:

case class Parameters(
  minTestsOk: Int,
  maxDiscardRatio: Float,  
  minSize: Int,
  maxSize: Int,
  rng: scala.util.Random,
  workers: Int
)

Configuration Example:

class ConfiguredPropertySpec extends Specification with ScalaCheckProperty {
  // Override default parameters  
  override implicit val defaultParameters = Parameters(
    minTestsOk = 500,
    maxDiscardRatio = 10.0f,
    minSize = 10,
    maxSize = 1000,
    workers = Runtime.getRuntime.availableProcessors
  )
  
  def is = s2"""
    Configured property tests
      expensive property    $expensiveProperty
    """
    
  def expensiveProperty = prop { (data: ComplexData) =>
    expensiveValidation(data) must beTrue
  }
}

PrettyDetails

Enhanced failure reporting for property tests.

trait PrettyDetails {
  def prettyFreqMap(freqMap: Map[Set[Any], Int]): String
  def prettyTestRes(testRes: Test.Result): String
  def prettyArgs(args: List[Arg[Any]]): String
}

Mockito Integration

Mockito

Integration with Mockito mocking framework.

trait Mockito {
  def mock[T: ClassTag]: T
  def mock[T: ClassTag](name: String): T
  def mock[T: ClassTag](defaultAnswer: Answer[_]): T
  def spy[T](realObject: T): T
  
  // Verification methods
  def verify[T](mock: T): T
  def verify[T](mock: T, mode: VerificationMode): T
  def verifyNoMoreInteractions(mocks: AnyRef*): Unit
  def verifyZeroInteractions(mocks: AnyRef*): Unit
  
  // Stubbing methods  
  def when[T](methodCall: T): OngoingStubbing[T]
  def doReturn(value: Any): Stubber
  def doThrow(throwable: Throwable): Stubber
  def doAnswer(answer: Answer[_]): Stubber
  def doNothing(): Stubber
}

Usage Example:

import org.specs2._
import org.specs2.mock.Mockito

class MockitoSpec extends Specification with Mockito { def is = s2"""
  Service with mocked dependencies
    should call repository save method    $callsSave
    should handle repository exceptions   $handlesException
  """
  
  def callsSave = {
    val mockRepo = mock[UserRepository]
    val service = new UserService(mockRepo)
    val user = User("john", "john@test.com")
    
    when(mockRepo.save(user)).thenReturn(user.copy(id = Some(1)))
    
    val result = service.createUser(user)
    
    result.id must beSome(1)
    verify(mockRepo).save(user)
  }
  
  def handlesException = {
    val mockRepo = mock[UserRepository] 
    val service = new UserService(mockRepo)
    val user = User("john", "john@test.com")
    
    when(mockRepo.save(user)).thenThrow(new DatabaseException("Connection failed"))
    
    service.createUser(user) must throwA[ServiceException]
  }
}

Advanced Mocking Features

Argument Matchers:

import org.mockito.ArgumentMatchers._

def usesArgumentMatchers = {
  val mockService = mock[EmailService]
  
  when(mockService.send(any[String], contains("@test.com"), anyInt()))
    .thenReturn(true)
    
  mockService.send("subject", "user@test.com", 1) must beTrue
  verify(mockService).send(eq("subject"), argThat(_.contains("@test.com")), gt(0))
}

Capturing Arguments:

import org.mockito.{ArgumentCaptor, Mockito}

def capturesArguments = {
  val mockRepo = mock[AuditRepository]
  val service = new UserService(mockRepo)
  val captor = ArgumentCaptor.forClass(classOf[AuditEvent])
  
  service.deleteUser(123)
  
  verify(mockRepo).save(captor.capture())
  val event = captor.getValue
  event.action must beEqualTo("DELETE")
  event.entityId must beEqualTo(123)
}

HamcrestMatcherAdapter

Integration with Hamcrest matchers for enhanced assertions.

trait HamcrestMatcherAdapter {
  def adapt[T](hamcrestMatcher: org.hamcrest.Matcher[T]): Matcher[T]
  implicit def hamcrestToSpecs2[T](hamcrestMatcher: org.hamcrest.Matcher[T]): Matcher[T]
}

Usage Example:

import org.specs2._
import org.specs2.mock.HamcrestMatcherAdapter
import org.hamcrest.Matchers._

class HamcrestSpec extends Specification with HamcrestMatcherAdapter { def is = s2"""
  Using Hamcrest matchers
    string contains check    $stringContains
    collection size check    $collectionSize
  """
  
  def stringContains = {
    "hello world" must adapt(containsString("world"))
  }
  
  def collectionSize = {
    List(1, 2, 3) must adapt(hasSize(3))
  }
}

Analysis Features

CompilerDependencyFinder

Code analysis using Scala compiler for dependency checking.

class CompilerDependencyFinder {
  def findDependencies(classNames: List[String]): List[Dependency]
  def findPackageDependencies(packageName: String): List[PackageDependency]
  def checkCircularDependencies(packages: List[String]): List[CircularDependency]
}

DependencyFinder

General interface for dependency analysis.

trait DependencyFinder {
  def getPackageDependencies(packageNames: List[String]): Operation[List[PackageDependency]]
  def getClassDependencies(className: String): Operation[List[ClassDependency]]
}

ClassycleDependencyFinder

Integration with Classycle for advanced dependency analysis.

class ClassycleDependencyFinder extends DependencyFinder {
  def checkArchitectureRules(rules: List[ArchitectureRule]): List[RuleViolation]
  def analyzeLayerDependencies(layers: List[Layer]): ArchitectureAnalysis
}

Usage Example:

import org.specs2._
import org.specs2.analysis._

class ArchitectureSpec extends Specification { def is = s2"""
  Architecture rules
    layers should respect dependencies    $layerDependencies
    no circular dependencies             $noCircularDeps
  """
  
  def layerDependencies = {
    val finder = new ClassycleDependencyFinder
    val rules = List(
      ArchitectureRule("service layer", "com.example.service", 
        canDependOn = List("com.example.domain", "com.example.repository"),
        cannotDependOn = List("com.example.web")),
      ArchitectureRule("web layer", "com.example.web",
        canDependOn = List("com.example.service", "com.example.domain"),
        cannotDependOn = List("com.example.repository"))
    )
    
    val violations = finder.checkArchitectureRules(rules)
    violations must beEmpty
  }
  
  def noCircularDeps = {
    val finder = new CompilerDependencyFinder
    val packages = List("com.example.service", "com.example.repository", "com.example.domain")
    val circular = finder.checkCircularDependencies(packages)
    circular must beEmpty
  }
}

GWT Integration

GWT Support

Integration for Google Web Toolkit projects.

trait GwtSpecification {
  def gwtSetup(): Unit
  def gwtTeardown(): Unit
  def runInGwtMode[T](test: => T): T
}

Note: GWT integration is provided for legacy support and may be deprecated in newer versions.

Testing Framework Integration

TestFramework Integration

Integration with SBT's test framework interface.

class Specs2Framework extends TestFramework {
  def name: String = "specs2"
  def fingerprints: Array[Fingerprint] = Array(
    SubclassFingerprint("org.specs2.Specification", false, true),
    SubclassFingerprint("org.specs2.mutable.Specification", false, true),
    SubclassFingerprint("org.specs2.Spec", false, true),
    SubclassFingerprint("org.specs2.mutable.Spec", false, true)
  )
}

Custom Test Interfaces

Creating custom test framework integrations:

class CustomRunner extends Runner2 {
  def run(eventHandler: EventHandler, loggers: Array[Logger], 
          continuation: Array[Task] => Unit, args: Array[String]): Unit = {
    // Custom runner implementation
  }
  
  def tasks(taskDefs: Array[TaskDef]): Array[Task] = {
    // Convert task definitions to executable tasks
  }
}

Best Practices

JUnit Integration

  1. Use @RunWith(classOf[JUnitRunner]) for IDE compatibility
  2. Combine with Maven/Gradle for build tool integration
  3. Generate JUnit XML reports for CI/CD integration
  4. Keep specification structure simple for JUnit compatibility

ScalaCheck Integration

  1. Start with simple properties and build complexity gradually
  2. Use meaningful property names for better failure reporting
  3. Configure appropriate test counts based on property complexity
  4. Leverage custom generators for domain-specific data
  5. Use labels (:| "label") for better failure diagnostics

Mockito Integration

  1. Mock external dependencies only, not domain objects
  2. Verify interactions that are important to the business logic
  3. Use argument captors for complex verification scenarios
  4. Reset mocks between tests when necessary
  5. Prefer real objects over mocks when possible

Architecture Testing

  1. Define clear layer boundaries and enforce them with analysis
  2. Check for circular dependencies regularly
  3. Use architecture rules to prevent regression
  4. Document architectural decisions in test specifications
  5. Run architecture tests as part of continuous integration

General Integration

  1. Choose appropriate integrations based on project needs
  2. Keep integration code simple and focused
  3. Document integration requirements and setup procedures
  4. Test integrations in isolation when possible
  5. Monitor integration performance and optimize as needed