How to test an object's private methods in Scala

后端 未结 3 1825
醉话见心
醉话见心 2021-01-07 09:24

I have an example object:

object Foo {
  private def sayFoo = \"Foo!\"
}

And I want to test the private sayFoo method without the following

相关标签:
3条回答
  • 2021-01-07 09:56

    It cannot be tested by any standard means.

    However, if you need to "cover" that section of code, then test the methods which use that private method in their implementation. It is indirect, granted, but it does effectively test the code.

    For example, in your class you could have another method:

    def dontSayBar = {
        sayFoo()
        println("Other processing...")
    }
    

    and this will "cover" the code in the private method if tested.

    P.S. A good rule of thumb is that if you see code that isn't showing up in a coverage run, then you probably don't actually need that code.

    0 讨论(0)
  • 2021-01-07 09:56

    Probably someone has done this already: How about an annotation macro @Testable that emits the object with a method that exposes the private members, but only when compiling for testing (and just the object otherwise).

    scala> object X { private def f = 42 ; def g = 2 * f }
    defined object X
    
    scala> X.g
    res1: Int = 84
    
    scala> object X { private def f = 42 ; def g = 2 * f ; def testable = new { def f = X.this.f } }
    defined object X
    
    scala> X.testable.f
    warning: there was one feature warning; re-run with -feature for details
    res2: Int = 42
    
    scala> object X { private def f = 42 ; def g = 2 * f ; def testable = new Dynamic { def selectDynamic(name: String) = X.this.f } }
    defined object X
    
    scala> X.testable.f
    warning: there was one feature warning; re-run with -feature for details
    res4: Int = 42
    

    Alternatively, it could emit a different object TestableX.

    0 讨论(0)
  • 2021-01-07 10:04

    As other said, there is no way to directly test a private method, especially of an object. However you can cheat by using reflection in order to bypass the restriction.

    I'm not sure if there is a cleaner way to do the same using Scala reflection API, however the following code using Java's Reflection does work:

    val m = Foo.getClass.getDeclaredMethod("sayFoo")
    val inst = Foo.getClass.getField("MODULE$").get()
    m.setAccessible(true)
    m.invoke(inst)
    

    First you need to get a reference to the sayFoo method. Next you need to obtain the reference to the singleton instance of the Foo object which is saved in the MODULE$ static field.

    Once you have both references you just need to tell the JVM to remove the access restriction from the method, making it accessible.

    This is clearly an ugly hack which, but if used sparingly and only for testing purposes may be useful.

    0 讨论(0)
提交回复
热议问题