您的位置:首页 > 其它

scala - Assertions and Unit Testing

2013-05-23 00:00 351 查看
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:

specs framework

ScalaCheck
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  scala