

NET framework, such as mutable dictionaries. While the previous Haskell example demonstrated that it's possible to write state-based unit tests in a functional style, when using F#, it sometimes make sense to leverage the object-oriented features already available in the.

These are all state-based tests that avoid over-specifying the interactions. Instead, these tests rely on setting up the initial state, exercising the System Under Test, and verifying the final state. The exact interactions between post and db aren't specified. Let ``Users don't connect when other user Id is invalid`` () = Property.check Gen.string (Range.linear 0 100) |> Gen.filter isIdInvalidĪll tests inspect the state of the Fake database after the calling the post function. Let ``Users don't connect when user Id is invalid`` () = Property.check Gen.string (Range.linear 0 100) |> Gen.filter isIdInvalid Let ``Users don't connect when other user doesn't exist`` () = Property.check Gen.int You can write the remaining three test cases in the same vein: The test verifies that the state of the database didn't change (that IsDirty is still false), and that actual represents a 400 Bad Request HTTP response. Since i is generated from the range 1 - 1,000,000, uniqueUserId is guaranteed to be different from otherUser.UserId. One way to get around that problem is to add the two numbers together. Since this is a property-based test, there's a risk that Hedgehog might generate a number i equal to otherUser.UserId. This is, however, a position I'm ready to reassess should the tests evolve to make this design awkward.Īs explained in the previous article, this test case requires an ID of a user that doesn't exist. You could consider this a bit of a hack, but I think it makes the intent of the test clear. The AddUser method sets IsDirty to true, so it's important to reset the flag before the act phase of the test. Once it's done with configuring the database, it sets IsDirty to false. This test adds one valid user to the Fake database. Let ``Users don't connect when user doesn't exist`` () = Property.check Gen.int One of these is when the first user doesn't exist: While there's one happy-path test case, there's four other test cases left. It also verifies that actual represents a 200 OK HTTP response. Finally, it verifies that the 'first' user's ConnectedUsers now contains the otherUser. It then calls the post function, passing the db.LookupUser and db.UpdateUser methods as arguments. This test first adds two valid users to the Fake database db. ( fun u tests in this article use 2.3.1, Unquote 4.0.0, and Hedgehog 0.7.0.0. Let ``Users successfully connect`` () = Property.check Option.exists | true, i -> match users.TryGetValue i with You could, then, choose to be pragmatic and base your Fake database on mutable state. F# is a functional-first language, but you can also write mutable code if need be. It's also possible to implement the State monad in F#, but there's less need for it. Like in the Haskell example, you can implement a Fake database in F#. Since functions are polymorphic, however, it's possible to replace them with Test Doubles.

The two function arguments lookupUser and updateUser represent interactions with a database. While the original C# example used Constructor Injection, the above post function uses partial application for Dependency Injection. Match connect with Ok u -> OK u | Error msg -> BadRequest msg | InvalidId -> "Invalid ID for other user."ĪddConnection user otherUser |> updateUser LookupUser otherUserId |> Result.mapError ( function LookupUser userId |> Result.mapError ( function Like in the previous Haskell example, in this article we'll start with the implementation, and then see how to unit test it. I explain the example in details in my two Clean Coders videos, Church Visitor and Preserved in translation. This article, like the others in this series, implements an operation to connect two users. The code shown in this article is available on GitHub. In this article, you'll see how to apply what you've learned in F#. In the previous article, you saw how to write state-based tests in Haskell.

This article is an instalment in an article series about how to move from interaction-based testing to state-based testing. While F# is a functional-first language, it's okay to occasionally be pragmatic and use mutable state, for example to easily write some sustainable state-based tests.
