JUnit3 Scala TestCase
There is number of new testing framework for Java and Scala. For example JUnit4, TestNG, Spec, ScalaTest... I never liked annotation driven test cases or 'story driven tests'. So I wrote my own TestCase which works with JUnit3 and can use some Scala features.
A few more reasons for my own solution:
- lot of complex ideas in other frameworks, I like to keep my tests simple
- most Scala frameworks does not integrate well with IDE
- lot of imports and extra typing
- big part of my project is in Java, Scala frameworks dont play well with Java.
So here is my solution. It is single class 150 lines long. It extends 'junit.framework.TestCase'. Usage is deadly simple:
class ScalaTestCaseExampleTest
extends ScalaTestCase{ //notice class it extends instead of 'TestCase'
// first test case demonstrating equal operators
// note 'test' prefix
def testOperators{
val s1 = "123456"
// !== operator is exactly same as !=,
// but if both side does not equal
// it throws exception which shows both values
assert(s1 !== 123456)
// === similar as !== operator
assert(s1 === "123456")
// ~== compares two double values and pass if are 'nearly' identical
assert(1d ~== 1d + 1e-10)
// ?< and ?> operator throw exception with both values, if assertion fails
assert( 1 ?< 12 )
assert( 12 ?> 1 )
}
//Intercept operation catches expected exceptions
def testIntercept{
intercept[NullPointerException]{
null.equals(null)
}
}
//Expect will throw exception if given block returns other value
def testExpect{
expect(1){
2-1
}
}
}
And here is core class responsible for this. It does not comes in jar, so put it in your source tree.
import junit.framework._
import scala.math._
/**
* JUnit test case which mimics Java behaviour. It adds
* '===', '!==', '~==', '?>' and '?<' operators which shows values of
* expressions on both sides.
* <p>
* TestCase inherits methods from Assert where are
* defined as static. Those are not visible to Scala code,
* so this class redefines them and calls original static
* methods.
*
* @author Jan Kotek
*
*/
abstract class ScalaTestCase extends TestCase{
/** class used in implicit conversion for === and !== operators */
protected class Operators(a1:Any){
def ===(a2:Any):Option[String] =
if(a1 == a2) None
else Some(a1+" != "+a2);
def !==(a2:Any):Option[String] =
if(a1 != a2) None
else Some(a1+" == "+a2);
def ~==(v2:Double):Option[String] = {
val v1 = a1.asInstanceOf[Double]
if(abs(v1-v2)<abs(max(v1,1)/1e7)) return None
else Some(v1+" not even close to "+v2);
}
}
/** implicit conversions which provides === and !== operators */
implicit def anyToOperators(a1:Any) = new Operators(a1);
/** class which privides ?>, ?< , ?>= and ?<= operators*/
protected class OrderedOperators[E](a1:Comparable[E]){
def ?< (that: E): Option[String] =
if(a1.compareTo(that)<0) None
else Some(a1+" >= "+that)
def ?> (that: E): Option[String] =
if(a1.compareTo(that)>0) None
else Some(a1+" <= "+that)
def ?<= (that: E): Option[String] =
if(a1.compareTo(that)<=0) None
else Some(a1+" > "+that)
def ?>= (that: E): Option[String] =
if(a1.compareTo(that)>=0) None
else Some(a1+" < "+that)
}
/** implicit conversions which provides ?> and ?< operators */
implicit def anyToOrderedOperators[E](a1:Comparable[E]) =
new OrderedOperators[E](a1);
implicit def anyToOrderedOperatorsInt(a1:Int) =
new OrderedOperators[java.lang.Integer](a1);
implicit def anyToOrderedOperatorsDouble(a1:Double) =
new OrderedOperators[java.lang.Double](a1);
implicit def anyToOrderedOperatorsFloat(a1:Float) =
new OrderedOperators[java.lang.Float](a1);
implicit def anyToOrderedOperatorsLong(a1:Long) =
new OrderedOperators[java.lang.Long](a1);
def fail(){
Assert.fail();
}
def fail(msg:String){
Assert.fail(msg);
}
def assert(b:Boolean):Unit = assert(b,"")
def assert(b:Boolean, msg:String):Unit ={
if(!b) throw new AssertionFailedError(msg);
}
def assert(error:Option[String]){
if(error!=None) throw new AssertionFailedError(error.get);
}
def assert(error:Option[String], msg:String){
if(error!=None) throw new AssertionFailedError(error.get+"; "+msg);
}
/**
* Expects exception to be thrown from code block, or fail
* Usage:
* intercept[NullPointerException]{
* null.equals(null
* }
*
*
*/
def intercept[T <: AnyRef](block: => Any)(implicit manifest: Manifest[T]): Unit = {
try{
block
}catch {
case u: Throwable => {
val clazz = manifest.erasure.asInstanceOf[Class[T]]
if (!clazz.isAssignableFrom(u.getClass))
throw u; //wrong type of exception, rethrow
}
}
}
/**
* Expect that block returns given value or fail.
* Usage:
* expect(2){
* 1+1
* }
*/
def expect(value:Any)(block: => Any) = assert(value === block)
}
Last modification: April 23 2012
blog comments powered by Disqus