1

I was wondering if there is a way to test a function embedded within another function in Scala? Or the only to test it is to lift it as a top level function??

  def processNumbers(n: Int): Int = {
    def isEven(num: Int): Boolean = num % 2 == 0
    if (isEven(n)) 0 else -1
  }

For example, in the code snippet above I would like to be able to unit test isEven without having to make it the top level function.

By "top-level", I meant writing it at the same level as the "parent" function. In this case it would look like:

  def isEven(num: Int): Boolean   = num % 2 == 0
  def processNumbers(n: Int): Int = if (isEven(n)) 0 else -1
iamsmkr
  • 800
  • 2
  • 10
  • 29

2 Answers2

3

Nested methods are not accessible outside of their parent method/function so they cannot be tested in separation. In this regard they are the the same group as private methods - you use them because it is convenient to extract some functionality to separate method and give it a name, but it is not useful on its own and it would not give you much value testing it outside of its context.

If something would make sens to be used outside of the parent method, it would make sense to make it a public method and test it.

Mateusz Kubuszok
  • 24,995
  • 4
  • 42
  • 64
  • I do agree to your point but I have stumbled upon a scenario where it makes all the sense to keep it as embedded method but the parent method also create a resource heavy service at runtime which is not really needed in my unit tests. – iamsmkr Nov 28 '22 at 14:55
  • Which is why I thought if there is a way to achieve testing embedded methods directly. What I had to do was to extract it as a package level private method to be able to test functionality. Guess that should be fine? – iamsmkr Nov 28 '22 at 14:56
  • 1
    @iamsmkr a method that creates a resource heavy service is probably a code smell anyways. – Luis Miguel Mejía Suárez Nov 28 '22 at 15:03
  • @LuisMiguelMejíaSuárez It is managed as a cats effect resource, so should be fine?? How would you create such an instance at runtime then? What could be done to avoid this code smell? – iamsmkr Nov 28 '22 at 15:05
  • 1
    @iamsmkr well you would need to add more details, but from what I am imagining you have a bug there. Because you are creating anew service instance everytime you call your logic. Rather the logic class should require the service on its constructor. – Luis Miguel Mejía Suárez Nov 28 '22 at 15:09
1

Inner methods are compiled as instance methods. When you compile your code using scalac and then decompile it again you'll see that the inner methods gets compiled as an instance methods.

public class StackoverflowQuestionClass {
  public int processNumbers(int n) {
    return this.isEven$1(n) ? 0 : -1;
  }

  private final boolean isEven$1(int num) {
    return num % 2 == 0;
  }
}

So its most likely possible to access this method using a reflection utility and actually make assertions. But I would strongly encourage you not to do so.

If you need to make assumptions about the way the code is compiled, while you're writing tests, you most likely are doing something wrong.

You should treat a method as a single unit of testing. If you really want to test the inner method you'll have to give up a bit of encapsulation and move the inner method outwards.

senjin.hajrulahovic
  • 2,961
  • 2
  • 17
  • 32
  • Tests using Reflection doesn't seem like that bad of an idea? Please see this https://www.artima.com/articles/testing-private-methods-with-junit-and-suiterunner. – iamsmkr Nov 29 '22 at 10:12
  • Or Using annotations that relaxes visibility for testing? https://stackoverflow.com/a/6913490/1879109 – iamsmkr Nov 29 '22 at 10:22