Scala features by its DSL like assertion and testing, it reads like nartual English and is especially friendly for test specfication writer (they don't need to be some expert in computing or programming language).
There are several Test framework that is available for the Scala programming language. this include
Scala Test
JUnit and TestNG
Specs
ScalaCheck
Using a assertion
scala has provided base assertion that helps to some test based assertions. the test method that you mihgt use include the following- assert
- ensuring
examples of the scala assertion on the element class and its accompanying object is as follow.
package assertionAndUnitTesting.assertions;
// file:
// elements.scala
// package:
// assertionAndUnitTestting.elements
// description:
// in this post, we will examine the scala assertions, the very basic construct which allows us to do pre/post condition check
object Element {
private class ArrayElement(val contents: Array[String]) extends Element
private class LineElement(
s : String) extends Element {
def contents : Array[String] = Array(s)
override def height : Int = 1
override def width : Int = s.length
}
private class UniformElement(
chr : Char,
override val width : Int,
override val height :Int
) extends Element {
// ch * width will fail,
// and the error message is not clear - type mismatch (expect String, Int provided) the REPL need to improve
// on it error handling.
private val line : String = chr.toString * width
override def contents : Array[String] = Array.fill(height)(line)
}
def elem(contents: Array[String]) : Element = new ArrayElement(contents)
def elem(s : String) : Element = new LineElement(s)
def elem(chr : Char, width : Int, height : Int) : Element= new UniformElement(chr, width, height)
}
import Element.elem
// contents: not defined, you need to declare your class as "abstract"
//
abstract class Element {
def contents : Array[String]
def height : Int = contents.length
def width = if (contents.length ==0 ) 0 else contents(0).length
def beside(that : Element) : Element = {
val this1 = this heighten that.height
val that1 = that heighten this.height
elem (
for ((line1, line2) <- this1.contents zip that1.contents) yield (line1 + line2)
)
}
def above(that : Element) : Element = {
val this1 = this widen that.width
val that1 = that widen this.width
assert(this1.width == that1.width) // assert that this1.width is equal to that1.width before above is called on the real contents.
elem(this1.contents ++ that1.contents)
}
def widen(w : Int) : Element =
if (w <= width) this
else {
val left = elem(' ', (w - width) /2 , height)
val right = elem(' ', (w - width + left.width), height)
left beside this beside right
} ensuring (w <= _.width) // ensure after widen, parameter w is less than the widen one.
def heighten(h : Int) : Element =
if (h <= height) this
else {
val top = elem(' ', width, (h - height) / 2)
val bottom = elem(' ', width, (h - height + top.height))
top above this above bottom
}
override def toString = contents mkString "\n"
}
ScalaTest
ScalaTest offers some basic means to write test in Scala and also it provides several bridges to other test framework such as Junit and TestNG..
a very straightfoward test case in ScalaTest is as follow.
// file:
// scalatest_testsuite.scala
// package:
// assertionAndUnitTesting.testing.scalatests
package assertionAndUnitTesting.testing.scalatests
import org.scalatest.Suite
// import assertionAndUnitTesting.testing.scalatests
import assertionAndUnitTesting.assertions._
import assertionAndUnitTesting.assertions.Element._
// extends the org.scalatest.Suite and define test method with testXXX names
class ElementSuite extends Suite {
def testUniformElement() {
val ele = elem('x', 2, 3 )
assert(ele.width == 2)
}
}
and scalatest also provides a different style of testing, it also provide a FunSuite, where you can override the test method and supply the a function value rather than methods.
// file:
// scalatest_funsuite.scala
// package:
// assertionAndUnitTesting.testing.scalatests
package assertionAndUnitTesting.testing.scalatests
import org.scalatest.FunSuite
import assertionAndUnitTesting.assertions._
import assertionAndUnitTesting.assertions.Element._
// test is passed in as a by-name parameter to the test method
class ElementSuite extends FunSuite {
test("elem result should have passed width") {
val ele = elem('x', 2, 3)
assert(ele.width == 2)
}
}
in this post, we will going to examine some of the common used test framework and show some guidelines on how to write Unit Test for Scala programming language.
Using Junit and TestNG
to use the JUnit, the following example shows that you can run junit scala test code with the Junit test runner.
// file
// junits_testcase.scala
package assertionAndUnitTesting.testing.junits
import junit.framework.TestCase
import junit.framework.Assert.assertEquals
import junit.framework.Assert.fail
import assertionAndUnitTesting.assertions.Element.elem;
class ElementTestCase extends TestCase {
def testUnitformElement() {
val ele = elem('x', 2, 3)
assertEquals(2, ele.width)
assertEquals(3, ele.height)
try {
elem('x', -2, 3)
fail()
}
catch {
case e : IllegalArgumentException => {
println("an IllegalArgumentException")
}
}
}
}
and you can also take advantage of the scala conciseness like assertion and others, you can use the JunitWrapperSuite offered by the SCala test.
// file
// junits_junit3suite.scala
package assertionAndUnitTesting.testing.junits
import junit.framework.TestCase
import junit.framework.Assert.assertEquals
import junit.framework.Assert.fail
import assertionAndUnitTesting.assertions.Element.elem;
import org.scalatest.junit.JUnit3Suite
class ElementSuite extends JUnit3Suite {
def testUniformElement() {
val ele = elem('x', 2, 3 )
assert(ele.width == 2)
expect (3) {
ele.height
}
intercept[IllegalArgumentException] {
elem('x', -2, 3)
}
}
}
informative failure reports.
the way how it report the error is less informative, you might get an error message such as "3 did not equal to 2" in the failure reports.
ScalaTest gives you the following construct to report meaningful infomraiton on the error/reports.
- expectResult - it is used to be expect
- intercept
an example is shown as follow.
// file
// scalatest_informative_failure_report.,scala
// package
package assertionAndUnitTesting.testing.scalatests
import org.scalatest.Suite
import assertionAndUnitTesting.assertions._
import assertionAndUnitTesting.assertions.Element._
class InformativeSuite extends Suite {
def testExpect() {
val ele = elem('x', 2, 3)
// expect(2) { // expect has been deprecated
expectResult(2) {
ele.width
}
}
def testInterceptException() {
intercept[IllegalArgumentException] {
// throw
throw new IllegalArgumentException("Argument should not be null")
elem('x', -2, 3 )
}
}
}
JUnit
Junit is a very successful frameowrk of writing test for java classes, and as we already know that scala will be running on the Java runtime, and scala will compile into classes which can be understood by the junit runtime.
// file
// junits_testcase.scala
package assertionAndUnitTesting.testing.junits
import junit.framework.TestCase
import junit.framework.Assert.assertEquals
import junit.framework.Assert.fail
import assertionAndUnitTesting.assertions.Element.elem;
class ElementTestCase extends TestCase {
def testUnitformElement() {
val ele = elem('x', 2, 3)
assertEquals(2, ele.width)
assertEquals(3, ele.height)
try {
elem('x', -2, 3)
fail()
}
catch {
case e : IllegalArgumentException => {
println("an IllegalArgumentException")
}
}
}
}
howeve, to run this test, you will need both to reference the ScalaTest and the Junit jars.
Also, ScalaTest provides a Junit test wrapper - JUnit3Suite, with which you can leverage the scaltest's conciseness.
// file
// junits_junit3suite.scala
package assertionAndUnitTesting.testing.junits
import junit.framework.TestCase
import junit.framework.Assert.assertEquals
import junit.framework.Assert.fail
import assertionAndUnitTesting.assertions.Element.elem;
import org.scalatest.junit.JUnit3Suite
class ElementSuite extends JUnit3Suite {
def testUniformElement() {
val ele = elem('x', 2, 3 )
assert(ele.width == 2)
expect (3) {
ele.height
}
intercept[IllegalArgumentException] {
elem('x', -2, 3)
}
}
}
TestNG
Run with Testng annotationed tests.// file
// testng_elementtests.scala
// package
// assertionAndUnitTesting.testing.testng
package assertionAndUnitTesting.testing.testng
import org.testng.annotations.Test
import org.testng.Assert.assertEquals
import assertionAndUnitTesting.assertions.Element.elem
class ElementTests {
@Test
def verifyUniformElement() {
val ele = elem('x', 2, 3)
assertEquals(ele.width, 2)
assertEquals(ele.height, 3)
}
@Test(
expectedExceptions = Array(classOf[IllegalArgumentException])
)
def elementShoudThrowIAE() {
elem('x', -2, 3)
}
}
To run the same test with the TestNG wrapper so to leverage the ScalaTest concise syntax, you can do .
// file
// testng_scalawrapper.scala
// package
// assertionAndUnitTesting.testing.testng
package assertionAndUnitTesting.testing.testng
import org.testng.annotations.Test
import org.testng.Assert.assertEquals
import org.scalatest.testng.TestNGSuite
import assertionAndUnitTesting.assertions.Element.elem
class ElementSuite extends TestNGSuite {
@Test
def verifyUniformElement() {
val ele = elem('x', 2, 3)
assertEquals(ele.width, 2)
expect(3) {
ele.height
}
intercept[IllegalArgumentException] {
elem('x', -2, 3)
}
}
}
Tests as specification
Test as specification is the "Behavior-driven development(BDD)" the emphasis is to write human-readable specification on teh expected behavior of code, and accompanying test that verify the code has the specified behavior. Scalatest includes several traits - WordSpec, FlatSpec, and FeatureSpec - which facilitate this style of testing.// file
// specification_flatspecification.scala
// package:
// assertionAndUnitTesting.testing.specifications
package assertionAndUnitTesting.testing.specifications
import assertionAndUnitTesting.assertions.Element.elem;
import org.scalatest.FlatSpec
import org.scalatest.matchers.ShouldMatchers
class ElementSuite extends FlatSpec with ShouldMatchers {
"A uniformElement" should
"having a width equal to the passed value" in {
val ele = elem('x', 2, 3)
ele.width should be (3)
}
it should "have a height equal to the passed value" in {
val ele = elem('x', 2, 3)
ele.height should be (3)
}
it should "throw an IAE if passed a negative width" in {
evaluating {
elem('x', -2, 3)
} should produce [IllegalArgumentException]
}
}
This is with the ShouldMatcher, and you can re-write the code to have the MustMatcher.
// file
// specification_mustmatcher.scala
// package:
// assertionAndUnitTesting.testing.specifications
package assertionAndUnitTesting.testing.specifications
import assertionAndUnitTesting.assertions.Element.elem;
import org.scalatest.FlatSpec
import org.scalatest.matchers.ShouldMatchers
import org.scalatest.matchers.MustMatchers
class MustElementSuite extends FlatSpec with MustMatchers {
"A uniformElement" should
"having a width equal to the passed value" in {
val ele = elem('x', 2, 3)
ele.width must be >= 0
ele.width must be (3)
}
it should "have a height equal to the passed value" in {
val ele = elem('x', 2, 3)
// ele must have height 3 // -overloaded method value must with override ... cannot be applied to Element.
var arr = new Array(3)
arr must have length 3
var map = Map('a' -> 1, 'b' -> 2)
map must contain key 'c'
ele.height must be (3)
}
it should "throw an IAE if passed a negative width" in {
evaluating {
elem('x', -2, 3)
} must produce [IllegalArgumentException]
}
}
There is a spec testing framework, which is similar to the MustMatcher and the ShouldMatcher , but it has different syntax, let' check this out.
you may find more details on the specs jar is available here - specs framework
// file
// specification_specframework.scala
// package:
// assertionAndUnitTesting.testing.specifications
package assertionAndUnitTesting.testing.specifications
import org.specs._
import assertionAndUnitTesting.assertions.Element.elem;
import org.scalatest.FlatSpec
import org.scalatest.matchers.ShouldMatchers
class ElementSpecification extends Specification {
"A uniformElement" should {
"having a width equal to the passed value" in {
val ele = elem('x', 2, 3)
ele.width must be_==(3)
}
"have a height equal to the passed value" in {
val ele = elem('x', 2, 3)
ele.height must be_==(3)
}
"throw an IAE if passed a negative width" in {
elem('x', -2, 3) must
throwA[IllegalArgumentException]
}
}
}
Property-based check
property based check let you specify the properties that the code under test must obey. you can download a test framework called ScalaCheck , you can download here - ScalaCheck
// file
// specification_propertybased.scala
// package:
// assertionAndUnitTesting.testing.propertybased
package assertionAndUnitTesting.testing.propertybased
import assertionAndUnitTesting.assertions.Element.elem;
import org.scalatest.WordSpec
import org.scalatest.prop.Checkers
import org.scalacheck.Prop._
// ==> is the "implication operator", which means if the
// left is true, then the right must hold true
class ElementSpecification extends WordSpec with Checkers {
"elem result" must {
"have passed with " in {
check ((w : Int) => w > 0 ==> (elem('x', w, 3).width == w))
}
"have passed height " in {
check ((h : Int) => h > 0 ==> (elem('x', 2, h).height == h))
}
}
}
[PLACEHOLDER]:
This is a placholder we will come back to it later.
References: