Search code examples
scalascalatestscalamock

Is it possible to mock a function that is defined within another function?


I have some functions that access a database, which I need to mock for testing purposes.

For ease of use, I would like to define these functions within another function, where I can leverage scope to reduce the number of arguments I have to pass.

I need to test the parent function, while mocking the nested functions.

Are there any tricks to mock functions that are nested?

As a secondary question, are there ways to mock functions when nested at arbitrary depth?

And a side note: my project is light enough I'm not even using classical mocking, just stackable traits like this blog post suggests; but for this question, any kind of mocking is fine.

Here is some very simple example code:

class Storage {
  def storeData(specId: Long, data: String): Unit = {
    val rawPath = "/path/to/file"

    def storeFsEntry: Unit = {
      // do stuff
    }

    def storeDbEntry: Unit = {
      // do stuff we need mocked
    }

    if ( specId == 1 )
    {
      storeDbEntry
      storeFsEntry
    }
  }
}

Solution

  • It's not possible, but you can define a trait and implement it inside your function (if you really want this logic been implemented inside):

    class Storage {
    
        trait Storing {
             def path: String //you have to define all free members
             def storeDbEntry: Unit
             def storeFsEntry: Unit
        }
    
        def storeData(specId: Long, data: String, storingDefault: Option[Storing] = None): Unit = {
    
           val myStoring = new Storing {
              ...implement it here
           }
    
           val storing = storingDefault getOrElse myStoring 
           import storing._
    
           if ( specId == 1 ) {
               storeDbEntry
               storeFsEntry
           }
        }
    }
    

    Your mock will be something like that:

    trait StorageMock extends Storage {
    
        override def storeData(specId: Long, data: String, storingDefault: Option[Storing] = None): Unit = {
            val storingOverride = new Storing { ... } //mocking functionality    
            super.storeData(specId, data, storingDefault orElse Some(storingOverride))
        }
    
    }
    
    new Storage with StorageMock
    

    You may also turn storingDefault into a member instead of function parameter.

    The reason why it's not possible to do same for inner functions is that they are private and also typecheck can't be performed on them (in comparison with inner traits).