1

In F# (and most of the functional languages) some codes are extremely short as follows:

let f = getNames
>> Observable.flatmap ObservableJson.jsonArrayToObservableObjects<string>

or :

let jsonArrayToObservableObjects<'t> =
    JsonConvert.DeserializeObject<'t[]> 
    >> Observable.ToObservable

And the simplest property-based test I ended up for the latter function is :

 testList "ObservableJson" [
        testProperty "Should convert an Observable of `json` array to Observable of single F# objects" <| fun _ -> 
            //--Arrange--
            let (array , json) = createAJsonArrayOfString stringArray

            //--Act--
            let actual = jsonArray
                         |> ObservableJson.jsonArrayToObservableObjects<string> 
                         |> Observable.ToArray 
                         |> Observable.Wait


            //--Assert--
            Expect.sequenceEqual actual sArray
    ]

Regardless of the arrange part, the test is more than the function under test, so it's harder to read than the function under test!

  • What would be the value of testing when it's harder to read than the production code?

On the other hand:

  • I wonder whether the functions which are a composition of multiple functions are safe to not to be tested?
  • Should they be tested at integration and acceptance level?
  • And what if they are short but do complex operations?
Mohsen
  • 4,000
  • 8
  • 42
  • 73

2 Answers2

1

Depends upon your definition of what 'functional programming' is. Or even more precise - upon how close you wanna stay to the origin of functional programming - math with both broad and narrow meanings.

Let's take something relevant to the programming. Say, mappings theory. Your question could be translated in such a way: having a bijection from A to B, and a bijection from B to C, should I prove that composition of those two is a bijection as well? The answer is twofold: you definitely should, and you do it only once: your prove is generic enough to cover all possible cases.

Falling back into programming, it means that pipe-lining has to be tested (proved) only once - and I guess it was before deploy to the production. Since that your job as a programmer to create functions (mappings) of such a quality, that, being composed with a pipeline operator or whatever else, the desired properties are preserved. Once again, it's better to stick with generic arguments rather than write tons of similar tests.

So, finally, we come down to a much more valuable question: how one can guarantee that some operation preserve some property? It turns out that the easiest way to acknowledge such a fact is to deal with types like Monoid from the great Haskell: for example, Monoind is there to represent any associative binary operation A -> A -> A together with some identity-element of type A. Having such a generic containers is extremely profitable and the best known way of being explicit in what and how exactly your code is designed to do.

Zazaeil
  • 3,900
  • 2
  • 14
  • 31
0

Personally I would NOT test it.

In fact having less need for testing and instead relying more on stricter compiler rules, side effect free functions, immutability etc. is one major reason why I prefer F# over C#.

of course I continue (unit)testing of "custom logic" ... e.g. algorithmic code

plainionist
  • 2,950
  • 1
  • 15
  • 27