Testing private methods that take generic type using PrivateMethodTester
Asked Answered
S

1

7

How can I test a private method that takes a generic type using privateMethodTester in scala?

Let's say I have the following method:

private def parseValueForJsonKeyWithReturnType[A: TypeTag](
   node: JsonNode, 
   key: String, 
   defaultValue: Option[A] = None): A = {

    val parsedValue = Option(node.get(key)).map(value => { 
      typeOf[A] match {
         case t if t =:= typeOf[String] =>
           value.textValue()
         case t if t =:= typeOf[Double] =>
           value.asDouble()
         case t if t =:= typeOf[Long] =>
           value.asLong()
         case _ => throw new RuntimeException(s"Doesn't support conversion to [type=${typeOf[A]}] for [key=${key}]")
       }
    })

    parsedValue.getOrElse(defaultValue.get).asInstanceOf[A]
  }

I can call the method like this

parseValueForJsonKeyWithReturnType[Boolean](jsonNode, key="hours")
parseValueForJsonKeyWithReturnType[String](jsonNode, key="hours")
parseValueForJsonKeyWithReturnType[Long](jsonNode, key="hours")

In the test, I'm trying to do

val parseValueForJsonKeyWithReturnTypeInt = PrivateMethod[Int]('parseValueForJsonKeyWithReturnType)
a[RuntimeException] shouldBe thrownBy (object invokePrivate parseValueForJsonKeyWithReturnType[Int](jsonNode, "total" , None))

to make sure it will throw Runtime exception for unsupported type

But I get this error:

error: value parseValueForJsonKeyWithReturnType of type SerializerTest.this.PrivateMethod[Int] does not take type parameters.

If i try without type parameters, build succeeds but i get illegalArgumentException

Expected exception java.lang.RuntimeException to be thrown, but java.lang.IllegalArgumentException was thrown. 

Cause: java.lang.IllegalArgumentException: Can't find a private method named: parseValueForJsonKeyWithReturnType

What am I doing wrong? Probably the syntax is wrong.

Sisterhood answered 26/6, 2018 at 4:40 Comment(2)
Just invoke the method without type parameter: object invokePrivate calculateTotalWithReturnType(jsonNode, "total" , None)Joinder
That didn't work. Test runs now but fails with "Cause: java.lang.IllegalArgumentException: Can't find a private method named: calculateTotalWithReturnType"Sisterhood
E
0

I got the same issue with "Can't find a private method named" on Scala 2.13 and ScalaTest 3.2.14. So i walked into the Scastie to make an example for GitHub issue. But there it actually worked!

The only thing i have in my code which i didn't add to this test was object extending class like this:

class WithPrivate
object WithPrivate extends WithPrivate

When i test just with object SomePrivateMethods with private methods - ScalaTest finds the private methods in all possible ways:

import org.scalatest.{PrivateMethodTester}
import org.scalatest.wordspec.AnyWordSpec
import org.scalatest.matchers.must.Matchers

// this will fail if you uncomment it with object extends lower in code
//class WithPrivate {
object WithPrivate {

  private def toString (i: Int): String = i.toString ()

  private def sumToString (i1: Int) (i2: Int): String = (i1 + i2).toString ()

  private def somethingToString [T] (t: T): String = t.toString ()

  private def somethingToSomething [I, O] (i: Seq [I]) (f: I => O): Seq [O] =
    i.map (f)
}

// uncomment this if you uncomment class WithPrivate
//object WithPrivate extends WithPrivate

class Test extends AnyWordSpec with Matchers with PrivateMethodTester {
  "test" must {

    "work with simple method" in {
      val method =
        PrivateMethod [String] (Symbol ("toString"))

      WithPrivate.invokePrivate (method (1)) mustBe "1"
    }

    "fail with name error on simple method with wrong arguments" in {
      val method =
        PrivateMethod [String] (Symbol ("toString"))

      // Can't find a private method named: toString
      WithPrivate.invokePrivate (method (1, 2)) mustBe "1"
    }

    "work with multiple arguments list" in {
      val method =
        PrivateMethod [String] (Symbol ("sumToString"))

      WithPrivate.invokePrivate (method (1, 2)) mustBe "3"
    }

    "work with generics" in {
      val method =
        PrivateMethod [String] (Symbol ("somethingToString"))

      WithPrivate.invokePrivate (method (1)) mustBe "1"
    }

    "work with high-order functions" in {
      val method =
        PrivateMethod [Seq [String]] (Symbol ("somethingToSomething"))

      def f: Int => String = _.toString

      WithPrivate.invokePrivate (method (Seq (1, 2), f)) mustBe List ("1", "2")
    }

  }
}

new Test ().execute ()

https://scastie.scala-lang.org/0eQtkQNzTNm5BNhDEIlIng

So if you are having such issue - check if you are testing object or class.

Exile answered 1/2, 2023 at 7:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.