
- Scala - Home
- Scala - Overview
- Scala - Features
- Scala - Environment Setup
- Scala - Build Tool (SBT)
- Scala - REPL
- Scala - Dot & Dotty
- Scala - Basic Syntax
- Scala - Hello World Program
- Scala - Identifiers
- Scala - Keywords
- Scala - Comments
- Scala - Code Blocks
- Scala - Semicolon
- Scala - Constructs
- Scala - Expressions
- Scala - Input and Output
- Scala - Optional Braces
- Scala - Underscore (_)
- Data Types and Variables
- Scala - Data Types
- Scala - Type Bounds
- Scala - Context Bound
- Scala - Variances
- Scala - Type Hierarchy
- Scala - Variables
- Scala - Variable Scopes
- Scala - Literals
- Scala - Numeric Types
- Scala - Boolean Types
- Scala - Char Type
- Scala - Unit Types
- Scala - Strings
- Scala - Arrays
- Scala - Null Type
- Scala - Nothing
- Scala - Any Type
- Scala - AnyRef Type
- Scala - Unified Types
- Scala - Dates and Times
- Scala - Ranges
- Scala - Multidimensional Arrays
- Scala - WrappedArray
- Scala - StringBuilder
- Scala - String Interpolation
- Scala - StringContext
- Scala - Type Casting
- Scala var vs val
- Scala Operators
- Scala - Operators
- Scala - Rules for Operators
- Scala - Arithmetic Operators
- Scala - Relational Operators
- Scala - Logical Operators
- Scala - Bitwise Operators
- Scala - Assignment Operators
- Scala - Operators Precedence
- Scala - Symbolic Operators
- Scala - Range Operator
- Scala - String Concatenation Operator
- Scala Conditional Statements
- Scala - IF ELSE
- Scala - IF-ELSE-IF-ELSE Statement
- Scala - Nested IF-ELSE Statement
- Scala Loop Statements
- Scala - Loop Statements
- Scala - while Loop
- Scala - do-while Loop
- Scala - Nested Loops
- Scala - for Loop
- Scala - break Statement
- Scala - yield Keyword
- Scala Classes & Objects
- Scala - Classes & Objects
- Scala - Constructors
- Scala - Auxiliary Constructor
- Scala - Primary Constructor
- Scala - This Keyword
- Scala - Nested Classes
- Scala - Getters and Setters
- Scala - Object Private Fields
- Scala - Singleton Object
- Scala - Companion Objects
- Scala - Creating Executable Programs
- Scala - Stateful Object
- Scala - Enumerations
- Scala - Polymorphism
- Scala - Access Modifiers
- Scala - Apply Method
- Scala - Update Methods
- Scala - UnapplySeq Method
- Scala - Inheritance
- Scala - Extending a Class
- Scala - Method Overloading
- Scala - Method Overriding
- Scala - Generic Classes
- Scala - Generic Functions
- Scala - Superclass Construction
- Scala Methods & Functions
- Scala - Methods
- Scala - Functions
- Scala - Methods vs Functions
- Scala - Main Methods
- Scala - Functions Call-by-Name
- Scala - Functions with Named Arguments
- Scala - Function with Variable Arguments
- Scala - Recursion Functions
- Scala - Default Parameter Values
- Scala - Functions without Parameters
- Scala - Implicit Parameters
- Scala - Higher-Order Functions
- Scala - Nested Functions
- Scala - Extension Methods
- Scala - Anonymous Functions
- Partially Applied Functions
- Scala - Lazy Val
- Scala - Pure Function
- Scala - Currying Functions
- Scala - Control Abstractions
- Scala - Corecursion
- Scala - Unfold
- Scala - Tail Recursion
- Scala - Infinite Sequences
- Scala - Dynamic Invocation
- Scala - Lambda Expressions
- Scala - Polymorphic Functions
- Scala Collections
- Scala - Collections
- Mutable and Immutable Collections
- Scala - Lists
- Scala - Sets
- Scala - Maps
- Scala - TreeMap
- Scala - SortedMap
- Scala - Tuples
- Scala - Iterators
- Scala - Options
- Scala - NumericRange
- Scala - Infinite Streams
- Scala - Parallel Collections
- Scala Advanced Types
- Scala - Union Types
- Scala - Intersection Types
- Scala - Type Aliases
- Scala - Structural Types
- Scala - Match Expression
- Scala - Singleton Type Operator
- Scala - Abstract Types
- Scala - Dependent Types
- Scala - Abstract Type Bounds
- Scala - Higher-Kinded Types
- Scala - Opaque Type Alias
- Scala - Path-Dependent Types
- Scala - Type Lambdas
- Scala - Type Inference
- Scala - Algebraic Data Types
- Scala Pattern Matching
- Scala - Pattern Matching
- Scala - Guards
- Scala - Variables in Patterns
- Scala - Type Patterns
- Scala - The Matchable Trait
- Scala - Matching Arrays
- Scala - Matching Lists
- Scala - Matching Tuples
- Scala - Exception Handling
- Scala - Extractors
- Scala - Pattern Bindings
- Scala - Regular Expressions
- Scala - Case Classes
- Scala - Partial Functions
- Scala - Packaging and Imports
- Scala - Implicit Imports
- Scala - Export Clauses
- Scala - Nested Packages
- Scala - Chained Packages
- Scala - Package Objects
- Scala Files I/O
- Scala - Files I/O
- Scala - Writing Files
- Scala - Listing Files
- Scala - Deleting Directories
- Scala - Check File Exists
- Scala Advanced Concepts
- Scala - Closures
- Scala - Futures
- Scala - Promises
- Scala - Traits
- Scala - Trait Mixins
- Scala - Layered Traits
- Scala - Trait Linearization
- Scala - Sealed Traits
- Scala - Transparent Traits
- Scala - Process Management
- Scala - Scaladoc
- Scala - Literal Type Arithmetic
- Scala - Inline keyword
- Scala - Def, Var & Val
- Scala - Dropped Features
- Scala Unit Testing
- Scala - Unit Testing
- Scala - uTest
- Scala - MUnit
- Scala - ScalaTest Runner
- Scala - ScalaMock
- Scala - JUnit
- Scala - Mocking
- Scala - BDD Testing
Scala - JUnit Testing
JUnit is a testing framework for Java applications. It is used to write and run repeatable automated tests for correctness of your code. You can use JUnit for testing Scala applications. There are various features and integration capabilities within Scala projects.
You can write unit tests and integration tests using JUnit. You can also test performance to validate the behavior and performance of your application.
JUnit 4 with Scala
Dependencies
You need to add the following dependency to your build.sbt file to use JUnit 4 in a Scala project -
libraryDependencies += "com.github.sbt" % "junit-interface" % "0.13.3" % "test"
Now, you can run JUnit tests from SBT.
Writing and Running Tests
Consider this example for a simple JUnit 4 test in Scala -
package com.example.scala import org.junit.Test import org.junit.Assert._ class IntJunitTests { @Test def testOneIsPositive(): Unit = { assertTrue(1 > 0) } @Test def testMinusOneIsNegative(): Unit = { assertTrue(-1 < 0) } }
You can use the test command to run the tests from the sbt shell -
sbt test
The output will be,
[info] Passed: Total 2, Failed 0, Errors 0, Passed 2 [success] Total time: 3 s, completed 08-Aug-2024, 11:45:48Îûam
Advanced Test Execution
You can also run specific tests using commands like testOnly -
sbt "testOnly com.example.scala.IntJunitTests"
The output will be,
[info] Passed: Total 2, Failed 0, Errors 0, Passed 2 [success] Total time: 0 s, completed 08-Aug-2024, 11:46:47Îûam
You can run a single test within a class -
sbt "testOnly -- *.IntJunitTests.testOneIsPositive"
The output will be,
[info] Passed: Total 1, Failed 0, Errors 0, Passed 1 [success] Total time: 0 s, completed 08-Aug-2024, 11:47:29Îûam
Test Suites
JUnit also supports test suites. You can group multiple test classes. For example,
package com.example.scala import org.junit.runner.RunWith import org.junit.runners.Suite @RunWith(classOf[Suite]) @Suite.SuiteClasses(Array(classOf[IntJunitTests], classOf[StringJunitTests])) class TypesTestSuite
You can run the test suite -
sbt "testOnly *.TypesTestSuite"
Custom Test Runner
Sometimes, you may need to customize the test runner to handle specific test execution requirements. You can create custom test runner, like this -
import org.junit.runner.RunWith import org.scalatestplus.junit.JUnitRunner @RunWith(classOf[JUnitRunner]) class CustomTestRunner extends JUnitSuite { // Custom test logic here }
JUnit 5 with Scala
Dependencies and Setup
JUnit 5 requires a different setup compared to JUnit 4. You need to add the following to your plugins.sbt -
resolvers += Resolver.jcenterRepo addSbtPlugin("net.aichler" % "sbt-jupiter-interface" % "0.8.3")
Now, you should have this following dependency in your build.sbt -
libraryDependencies += "net.aichler" % "jupiter-interface" % "0.8.3" % Test
Writing JUnit 5 Tests
Consider this example of using JUnit 5 in Scala -
package com.example.scala import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test class ExampleTests { @Disabled("Not implemented yet") @Test def testNotImplemented(): Unit = {} }
Now, you can run the tests using -
sbt test
The output will be,
[success] Total time: 3 s, completed 08-Aug-2024, 11:52:25Îûam
Test Lifecycle Management
JUnit 5 provides annotations for managing the lifecycle of tests, like, @BeforeAll, @AfterAll, @BeforeEach, and @AfterEach. Here is how you can use these in Scala -
package com.example.scala import org.junit.jupiter.api.{BeforeAll, AfterAll, BeforeEach, AfterEach, TestInstance, Test} import org.junit.jupiter.api.TestInstance.Lifecycle @TestInstance(Lifecycle.PER_CLASS) class LifecycleTests { @BeforeAll def beforeAll(): Unit = { println("Before all tests") } @AfterAll def afterAll(): Unit = { println("After all tests") } @BeforeEach def beforeEach(): Unit = { println("Before each test") } @AfterEach def afterEach(): Unit = { println("After each test") } @Test def testExample(): Unit = { println("Executing test") } }
Note that you should have these dependencies in your build.sbt file -
scalaVersion := "2.13.14" libraryDependencies ++= Seq( "org.junit.jupiter" % "junit-jupiter-api" % "5.8.1" % "test", "org.junit.jupiter" % "junit-jupiter-engine" % "5.8.1" % "test", "org.junit.platform" % "junit-platform-launcher" % "1.8.1" % "test" ) testFrameworks += new TestFramework("munit.Framework")
Now, you can compile and run test using these commands -
sbt clean compile sbt run
The output will be,
[success] Total time: 3 s, completed 08-Aug-2024, 12:00:04Îûpm
ScalaTest Integration
Using ScalaTest with JUnit
ScalaTest provides more readable and concise assertions. You can use ScalaTest assertions with JUnit, mix in AssertionsForJUnit. For example,
package com.example.scala import org.scalatestplus.junit.AssertionsForJUnit import org.junit.Assert._ import org.junit.Test class ExampleSuite extends AssertionsForJUnit { @Test def verifyEasy(): Unit = { assertEquals("ScalaTest is easy!", "ScalaTest is easy!") } @Test def verifyFun(): Unit = { assert("ScalaTest is fun!" == "ScalaTest is fun!") } }
You need to add the following dependencies to your build.sbt -
import Dependencies._ ThisBuild / scalaVersion := "2.13.14" lazy val root = (project in file(".")) .settings( name := "Demo", version := "0.1", libraryDependencies ++= Seq( "org.scalatest" %% "scalatest" % "3.2.12" % "test", "com.github.sbt" % "junit-interface" % "0.13.3" % "test", "org.scalatestplus" %% "junit-4-12" % "3.2.2.0" % "test" ) )
The output will be,
[info] Passed: Total 2, Failed 0, Errors 0, Passed 2 [success] Total time: 1 s, completed 08-Aug-2024, 2:14:31Îûpm
Combining ScalaTest and JUnit
You can write tests that can be run by both JUnit and ScalaTest. For example,
import org.scalatestplus.junit.JUnitSuite import org.junit.Assert._ import org.junit.Test import org.junit.Before class CombinedSuite extends JUnitSuite { var sb: StringBuilder = _ var lb: ListBuffer[String] = _ @Before def initialize(): Unit = { sb = new StringBuilder("ScalaTest is ") lb = new ListBuffer[String] } @Test def verifyEasy(): Unit = { sb.append("easy!") assertEquals("ScalaTest is easy!", sb.toString) assertTrue(lb.isEmpty) lb += "sweet" try { "verbose".charAt(-1) fail() } catch { case e: StringIndexOutOfBoundsException => // Expected } } @Test def verifyFun(): Unit = { sb.append("fun!") assert(sb.toString === "ScalaTest is fun!") assert(lb.isEmpty) lb += "sweeter" intercept[StringIndexOutOfBoundsException] { "concise".charAt(-1) } } }
So, there can be flexibility in using either test framework.
Example: Testing a Calculator
Consider this test a simple calculator application -
package com.example.scala import org.junit.Test import org.junit.Assert._ class CalculatorTests { class Calculator { def add(a: Int, b: Int): Int = a + b def subtract(a: Int, b: Int): Int = a - b def multiply(a: Int, b: Int): Int = a * b def divide(a: Int, b: Int): Int = if (b != 0) a / b else throw new ArithmeticException("Division by zero") } val calculator = new Calculator @Test def testAddition(): Unit = { assertEquals(5, calculator.add(2, 3)) } @Test def testSubtraction(): Unit = { assertEquals(1, calculator.subtract(3, 2)) } @Test def testMultiplication(): Unit = { assertEquals(6, calculator.multiply(2, 3)) } @Test(expected = classOf[ArithmeticException]) def testDivisionByZero(): Unit = { calculator.divide(1, 0) } @Test def testDivision(): Unit = { assertEquals(2, calculator.divide(6, 3)) } }
Example: Testing a Simple Scala Class
Consider this example of testing a simple Scala class -
package com.example.scala import org.junit.Test import org.junit.Assert._ class Person(val name: String, val age: Int) class PersonTests { @Test def testPersonCreation(): Unit = { val person = new Person("John Doe", 30) assertEquals("John Doe", person.name) assertEquals(30, person.age) } @Test def testPersonAge(): Unit = { val person = new Person("Jane Doe", 25) assertTrue(person.age > 20) } }