In the process of writing the Scala Cookbook for Scala 3 (#ad), there were days when it was hard to find a working testing framework. As a result, I decided to write my own Scala 3 testing framework. Calling it a framework is a bit of a joke, as it’s only about 88 lines of source code. I named that library SimpleTest, and you can find it at github.com/alvinj/SimpleTest.
So I can easily find the source code later, I’m putting Version 0.3 of the source code here:
package com.alvinalexander.simpletest /** * All of the source code in this file is distributed under the * GNU General Public License v3.0. See the LICENSE file in this * project, or https://choosealicense.com/licenses/gpl-3.0 for * more details. * * Created by Alvin J. Alexander, https://alvinalexander.com */ package support { val AnsiRed = "\u001B[31m" val AnsiGreen = "\u001B[32m" val AnsiYellow = "\u001B[33m" val AnsiReset = "\u001B[0m" def printRed(s: String) = println(s"${AnsiRed}${s}${AnsiReset}") def printGreen(s: String) = println(s"${AnsiGreen}${s}${AnsiReset}") def printYellow(s: String) = println(s"${AnsiYellow}${s}${AnsiReset}") } import support._ object SimpleTest: private def _True( blockOfTestCode: => Boolean, trueString: String, falseString: String ): Unit = if blockOfTestCode then printGreen(trueString) else printRed(falseString) end if /** * Assert that an expression is true. * @param blockOfTestCode The expression to test. * @param num The test number. */ def True(blockOfTestCode: => Boolean, num: Int): Unit = _True( blockOfTestCode, s"(true) test $num", s"(false) test $num" ) /** * Assert that an expression is true. * @param blockOfTestCode The expression to test. * @param desc The test description. */ def True(blockOfTestCode: => Boolean, desc: String): Unit = _True( blockOfTestCode, s"(true) $desc", s"(false) $desc" ) private def _False( blockOfTestCode: => Boolean, trueString: String, falseString: String ): Unit = if blockOfTestCode == false then printGreen(trueString) else printRed(falseString) end if /** * Assert that an expression is false. * @param blockOfTestCode The expression to test. * @param num The test number. */ def False(blockOfTestCode: => Boolean, num: Int): Unit = _False( blockOfTestCode, s"(true) test $num", s"(false) test $num" ) /** * Assert that an expression is false. * @param blockOfTestCode The expression to test. * @param desc The test description. */ def False(blockOfTestCode: => Boolean, desc: String): Unit = _False( blockOfTestCode, s"(true) $desc", s"(false) $desc" ) /** * Assert that the two given values are equal (`==`). * @param expected The expected result. * @param actual The actual result. * @param desc A description of the test. */ def Equals(expected: Any, actual: Any, desc: String): Unit = if expected == actual then printGreen(s"(true) $desc") else printRed(s"(false) EXPECTED: ($expected), ACTUAL: ($actual), DESC: $desc") end if def Todo(desc: String): Unit = printYellow(s"TODO: $desc") /** * This is a different approach so you don’t have to supply * a description. Just let this object keep track of the * test numbers for you. */ private var testCount = 0 def True(blockOfTestCode: => Boolean): Unit = testCount += 1 if blockOfTestCode then printGreen(s"(true) test $testCount") else printRed(s"(false) test $testCount") end if def False(blockOfTestCode: => Boolean): Unit = testCount += 1 if blockOfTestCode == false then printGreen(s"(true) test $testCount") else printRed(s"(false) test $testCount") end if /** * Assert that the two given values are equal (`==`). * @param expected The expected result. * @param actual The actual result. */ def Equals(expected: Any, actual: Any): Unit = //TODO decide on what output you want to see here testCount += 1 if expected == actual then printGreen(s"(true) EXPECTED: ($expected), ACTUAL: ($actual)") else printRed(s"(false) EXPECTED: ($expected), ACTUAL: ($actual)") end if end SimpleTest
That code can use some refactoring, but (a) it did what I needed it to, and (b) writing the Scala Cookbook was the highest priority at the time I wrote this code. I know my OKRs. ;)
Demonstration code
Also for myself, this code shows how to use that library in Scala 3:
package tests import com.alvinalexander.simpletest.SimpleTest._ @main def tests = // run tests and give them descriptions True(1 == 1, "1 == 1 (expecting green)") True(1 == 2, "1 == 2 (expecting red)") False(1 == 2, "1 == 2 (expecting green)") False(1 == 1, "1 == 1 (expecting red)") Equals(1, 1, "i expect green") Equals(1, 2, "i expect red here") Todo("don’t forget to test 2 == 3") // run tests but only give them numbers True(1 == 1, 1) True(2 == 2, 2) True(3 == 3, 3) False(1 == 7, 4) False(1 == 8, 5) False(6 == 60, 6) // numbering is too much work, just let the object // keep track of the test numbers True(1 == 1) False(1 == 2) Equals(1, 1) package t1 { import com.alvinalexander.simpletest.SimpleTest._ @main def tests = // use `True` to assert that an expression is true True(1 == 1, "1 == 1 (expecting green)") True(1 == 2, "1 == 2 (expecting red)") // use `False` to assert that an expression is false False(1 == 2, "1 == 2 (expecting green)") False(1 == 1, "1 == 1 (expecting red)") // use `Equals` to assert that two objects are `==` Equals(1, 1, "i expect green") Equals(1, 2, "i expect red here") // in case you want to note tests that you intend to run Todo("don’t forget to test 2 == 3") // just run some tests and simpletest will keep track of // the test numbers for you True(1 == 1) False(1 == 2) Equals(1, 1) }
If you need a real testing framework for Scala 3, I recommend something like ScalaTest or mUnit, but for the purposes of the Scala Cookbook I needed something that worked every day, so I wrote this.