1

I want to unit test a function that returns a function. This is a simplified example:

func someFunction(flag: Bool) -> () -> Void {
    if flag {
        return { print("True") }
    } else {
        return { print("False") }
    }
}

What would be the best approach?

Other languages offer a toString option, where you could check the string representation, but Swift prints "(Function)" if I were to do something like:

let output = someFunction(flag: true)
print(output)
gmoraleda
  • 1,731
  • 1
  • 17
  • 44
  • 2
    You are returning a closure. So call it with `output()`? But, you are alreading printing inside it, that's strange... Don't you want to return a `String` instead: `() -> String`? – Larme Sep 01 '21 at 10:19
  • 1
    unit test the behavior of the returned function. In this case, assuming you meant to print rather than return, you could have your stdout redirected to a file, and check the file after calling it. – Abel Sep 01 '21 at 10:20
  • I want to test the returned function being correct, no it's implementation. So, for this example, my test should pass if: `someFunction(flag: true) == { print("True" }` – gmoraleda Sep 01 '21 at 10:25
  • 1
    then you need to return a named function such as someFunction. you can then check if the returned function is equal to the named one. – Abel Sep 01 '21 at 10:28
  • 1
    https://stackoverflow.com/questions/45270371/in-swift-3-what-is-a-way-to-compare-two-closures ? That's some work to compare two closure. Also, `{ var a = 0; a +=1; print("True") }`, is it the same as `{ print("True") }` I mean, the "a" stuff doesn't affect the rest of the main "work" which is in your case the print... – Larme Sep 01 '21 at 10:37
  • You might write a UI Test, or redirect stdout to your own string buffer, then check this ... or, much more easier: make your function `someFunction` _pure_, which returns a String, as already suggested. – CouchDeveloper Sep 01 '21 at 13:33
  • The print inside the closure is unrelated to what I want to achieve. Was just an example (a bad one, I guess). It could as well be `return { !flag }` It is about comparing closures, but as @Larme pointed out, it is not possible in Swift. – gmoraleda Sep 01 '21 at 19:20

1 Answers1

2

Other languages offer a toString option, where you could check the string representation

You wouldn’t actually want this, even if it were possible.

By analogy, would you ever write a test like this?

test() {
    XCTAssertEqual(someFunction.toString(), """
        func someFunction(flag: Bool) -> () -> Void {
            if flag {
                return { print("True") }
            } else {
                return { print("False") }
            }
        }
    """)
}

The problem of course is that such a test doesn’t actually test anything, at all!

  1. It makes no specific claims as to the desired functionality of the system under test (SUT), so it’s not useful as a specification of your system
  2. It’s no simpler than the SUT it tests, meaning it gives you no confidence that you didn’t just make the same mistake twice.
  3. It’s also incredibly brittle, in any tiny refactoring of the implementation code will needlessly break the test, even if the refactoring made no behavioural changes. That’s the worst kind of test, because it’s expensive to maintain over time.

You should be testing the behaviour of the returned closure, just like any other SUT.

Alexander
  • 59,041
  • 12
  • 98
  • 151