For Java there’s JUnit and for Scala there’s Scalatest. As soon as I started learning Scala, I had to find a testing framework for unit testing. Unit testing should be easy and Scalatest is easy to set up and start using in Eclipse. It will use JUnit4 to actually run the tests using Scalatest as a wrapper.
To use Scalatest you need to add the scalatest JAR and also the JUnit4 library as shown here:
You can get the scalatest JAR from the Scalatest site. The JUnit4 library is part of your Eclipse installation now. Once you’ve made these available on your CLASSPATH, you’re ready to start testing.
What’s really nice about Scalatest is the language and the semantics of how your tests are called or verified. First, let me show you two Scala modules, one is a Scala object for Currency (the token of exchange) and the other a class to represent a particular sum of Money in a particular currency. Those listings are shown here:
Currency.scala
/**
* Copyright 2011 Cape Henry Technologies Inc.
*
* Licensed under the Apache License, Version 2.0
* (the "License"); You may not use this file except
* in compliance with the License. You may obtain a
* copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*
*/
package com.cht.model.financial
object Currency {
var currencyCode = "USD"
val codes = List("AED","AFN","ALL","AMD","AOA","ARS","AUD","AWG","AZN",
"BAM","BBD","BDT","BGN","BHD","BIF","BMD","BND","BOB",
"BOV","BRL","BSD","BTN","BWP","BYR","BZD","CAD","CDF",
"CHE","CHF","CHW","CLF","CLP","CNY","COP","COU","CRC",
"CUC","CUP","CVE","CZK","DJF","DKK","DOP","DZD","EGP",
"ERN","ETB","EUR","FJD","FKP","GBP","GEL","GHS","GIP",
"GMD","GNF","GTQ","GYD","HKD","HNL","HRK","HTG","HUF",
"IDR","ILS","INR","ISK","JMD","JOD","JPY","KES","KGS",
"KHR","KMF","KRW","KWD","KYD","KZT","LAK","LBP","LKR",
"LRD","LSL","LTL","LVL","LYD","MAD","MDL","MDA","MDL",
"MGA","MKD","MMK","MNT","MOP","MRO","MUR","MVR","MWK",
"MXN","MXV","MYR","MZN","NAD","NGN","NIO","NOK","NPR",
"NZD","OMR","PAB","PEN","PGK","PHP","PKR","PLN","PYG",
"QAR","RON","RSD","RUB","RWF","SAR","SBD","SCR","SDG",
"SEK","SGD","SHP","SLL","SOS","SRD","SSP","STD","SYP",
"SZL","THB","TJS","TMT","TND","TOP","TRY","TTD","TWD",
"TZS","UAH","UGX","USD","USN","USS","UYI","UYU","UZS",
"VEF","VND","VUV","WST","XAF","XAG","XAU","XBA","XBB",
"XBC","XBD","XCD","XDR","XFU","XOF","XPD","XPF","XPT",
"XTS","XXX","YER","ZAR","ZMK","ZWL")
def validateCurrencyCode(isoCode:String):Boolean = {
val iter = codes.toIterator
var retVal = false
for (code <- iter)
if (code == isoCode)
retVal = true
return retVal
}
}
Money.scala
/**
* Copyright 2011 Cape Henry Technologies Inc.
*
* Licensed under the Apache License, Version 2.0
* (the "License"); You may not use this file except
* in compliance with the License. You may obtain a
* copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*
*/
package com.cht.model.financial
import com.cht.constant.FinancialConstants
/**
* Scala class representation of Money. Currency of exchange is validated and set.
* Transactions between Monies of different currencies are NOT allowed.
*/
class Money (repr:String, format:Int, dp:Int, currCode: String) {
var currencyCode:String = currCode // default
var allNumericAmount:String = "" // e.g. "234525" = $2,345.25
var decimalAmount:String = "" // e.g. "2345.25"
var wholeNumberPortion:Int = 0 // e.g. 2345 (taxes maybe? round up?)
var decimalPortion:Int = 0 // e.g. 25 however if 2 would be represented by String as "02"
var displayAmount:String = "" // e.g. $2,345.25 or ($345.75) negative
var digitPrecision:Int = 2 // default, but could be 5 e.g. for interest rate calculations
var sign:String = "+" // default
if (!Currency.validateCurrencyCode(currCode))
throw new IllegalArgumentException("Currency code is not one of the ISO-4217 accepted values.")
if (dp != 2)
digitPrecision = dp // override or change digitPrecision if needed
var lessDp = 0
if (format == FinancialConstants.NUMERIC_ONLY) {
allNumericAmount = repr
lessDp = repr.length - dp
decimalPortion = repr.substring(lessDp, lessDp + dp).toInt
}
if (format == FinancialConstants.NUMERIC_DEC) {
decimalAmount = repr
lessDp = repr.length - dp - 1
decimalPortion = repr.substring(lessDp + 1, lessDp + dp + 1).toInt
}
wholeNumberPortion = repr.substring(0,lessDp).toInt
displayAmount = "$" + wholeNumberPortion + "." + decimalPortion // fix comma on thousands later
}
Now for the fun stuff, the testing. The test module is not complete but it will show you some of the really cool features of Scalatest. I've highlighted a few lines with the nice test language semantics. Notice that the "if" method arguments are Strings that you create. These Strings will show in JUnit for your tests. Here's some test code.
package com.cht.model.financial
import org.scalatest.junit.JUnitRunner
import org.junit.runner.RunWith
import org.scalatest.matchers.ShouldMatchers
import org.scalatest.Spec
import com.cht.constant._
@RunWith(classOf[JUnitRunner])
class PackageTests extends Spec with ShouldMatchers {
describe("Money:") {
it("should create and display $2345.56 from 234556 input") {
val money = new Money("234556", FinancialConstants.NUMERIC_ONLY, 2, "USD")
money.displayAmount should be ("$2345.56")
}
}
describe("Money:") {
it("should create and display $350.75 from 350.75 input") {
val money = new Money ("350.75", FinancialConstants.NUMERIC_DEC, 2, "USD")
money.displayAmount should be ("$350.75")
}
}
describe("Money:") {
it("should validate ZZZ as an unacceptable ISO-4217 currency code, throw IllegalArgumentException") {
val thrown = intercept[java.lang.IllegalArgumentException] {
val money = new Money ("350.75", FinancialConstants.NUMERIC_DEC, 2, "ZZZ")
}
assert(thrown.getMessage === "Currency code is not one of the ISO-4217 accepted values.")
}
}
describe("Currency:") {
it("should validate EUR as an acceptable ISO-4217 currency code") {
Currency.validateCurrencyCode("EUR") should be (true)
}
}
describe("Currency:") {
it("should validate ZWL as an acceptable ISO-4217 currency code") {
Currency.validateCurrencyCode("ZWL") should be (true)
}
}
describe("Currency:") {
it("should validate LBP as an acceptable ISO-4217 currency code") {
Currency.validateCurrencyCode("LBP") should be (true)
}
}
describe("Currency:") {
it("should validate ABC as an unacceptable ISO-4217 currency code") {
val thrown = intercept[org.scalatest.TestFailedException] {
Currency.validateCurrencyCode("ABC") should be (true)
}
assert(thrown.getMessage === "false was not true")
}
}
}