We all know that testing code can be done in different ways. This pill is not to explain which is the best way to see if your Scala project is working as it should. But it will provide some tips and tricks for testing public, private, and protected methods.
Public methods are the functions inside a class, that can be called from outside, through the instantiated object. Public method testing is no rocket science. In Scala, the use of Matchers
and Clues
is needed in order to understand what is wrong.
Imagine we want to test a MathUtils class that has simple methods min and max:
class MathUtils {
def min(x: Int, y: Int): Int = if (x <= y) x else y
def max(x: Int, y: Int): Int = if (x >= y) x else y
}
This is how your test should look like:
import org.scalatest.AppendedClues.convertToClueful
import org.scalatest.matchers.should.Matchers
import org.scalatest.flatspec.AnyFlatSpec
class MathUtilsTest extends AnyFlatSpec with Matchers {
"MathUtils" should "compute min correctly" in {
val min = 10
val max = 20
val mathUtils = new MathUtils()
mathUtils.min(min, max) shouldBe min withClue s"Min is not $min"
}
it should "compute max correctly" in {
val min = 10
val max = 20
val mathUtils = new MathUtils()
mathUtils.max(min, max) shouldBe max withClue s"Max is not $max"
}
}
Private methods are the methods that cannot be accessed in any other class than the one in which they are declared.
Testing these functions is way more tricky. You have different ways of proceeding: copy and paste the implementation in a test class (which is out of the table), use Mockito
, or try with PrivateMethodTester
.
Let’s write a private method on the class MathUtils:
class MathUtils {
def min(x: Int, y: Int): Int = if (x <= y) x else y
def max(x: Int, y: Int): Int = if (x >= y) x else y
private def sum(x: Int, y: Int): Int = {
x + y
}
def sum(x: Int, y: Int, z: Int): Int = {
val aux = sum(x, y)
sum(aux, z)
}
}
PrivateMethodTester is a trait that facilitates the testing of private methods. You have to mix it in your test class in order to take advantage of it.
import org.scalatest.AppendedClues.convertToClueful
import org.scalatest.matchers.should.Matchers
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.PrivateMethodTester
class MathUtilsPrivateTest extends AnyFlatSpec with Matchers with PrivateMethodTester {
"MathUtils" should "compute sum correctly" in {
val x = 1
val y = 2
val mathUtils = new MathUtils()
val sumPrivateMethod = PrivateMethod[Int]('sum)
val privateSum = mathUtils invokePrivate sumPrivateMethod(1, 2)
privateSum shouldBe (x + y) withClue s"Sum is not is not ${x + y}"
}
}
In val sumPrivateMethod = PrivateMethod[Int]('sum)
we have different parts:
[Int]
is the return type of the method(’sum)
is the name of the method to callIn mathUtils invokePrivate sumPrivateMethod(x, y)
you can collect the result in a val
to compare and understand if it’s working properly. You need to use an instance of the class/object to invoke the method, otherwise, it will not find it.
A protected method is like a private method in that it can only be invoked from within the implementation of a class or its subclasses.
For example we decide to make sum
method protected instead of private. Class MathUtils
would look like this:
class MathUtils {
def min(x: Int, y: Int): Int = if (x <= y) x else y
def max(x: Int, y: Int): Int = if (x >= y) x else y
protected def sum(x: Int, y: Int): Int = x + y
}
If we create a new object from MathUtils
and try to call the sum
method, it will throw a warning saying that ‘sum is not accessible from this place’
But don’t worry, we have a solution for that as well.
We can write a subclass specific for this test and override the method since it can be invoked through the implementation of its subclasses.
class MathUtilsTestClass extends MathUtils {
override def sum(x: Int, y: Int): Int = super.sum(x, y)
}
class MathUtilsProtectedTest extends AnyFlatSpec with Matchers {
"MathUtils" should "compute sum correctly" in {
val x = 1
val y = 2
val mathUtilsProtected = new MathUtilsTestClass()
mathUtilsProtected.sum(x, y) shouldBe (x + y) withClue s"Sum is not is not ${x + y}"
}
}
Now you can test the different types of methods in your Scala project: public, private, and protected. For more information about Scala, functional programming, and style, feel free to ask us or check out our other pills!
This site uses Akismet to reduce spam. Learn how your comment data is processed.
Leave a Reply