How do I make my F# application testable? The application is written mostly using F# functions, and records.
I am aware of How to test functions in f# with external dependencies and I'm aware of the various blog posts that show how easy this is done when your interface only has one method.
Functions are grouped in modules similar to how I would group method in C# classes.
My problems is how do I replace certain "abstractions" when running tests. I need to do this since these abstractions read/write to the DB, talk to services over the network etc. An example of such abstractions is the below repository for storing and fetching people and companies (and their rating).
How do I replace this code in testing? The function calls are hard coded, similar to static method calls in C#.
I have a few posibilities in mind, but not sure if my thinking is too colored of my C# background.
-
I can implement my modules as interfaces and classes. While this is still F# I feel this is a wrong approach, since I then loose a lot of benefits. This is also argued for in http://ift.tt/1M5fxUJ
-
The code that calls eg. our
PersonRepo
could take as argument function pointers to all the functions of thePersonRepo
. This however, quickly accumulate to 20 or more pointers. Hard for anyone to overview. It also makes the code base fragile, as for every new function in say ourPersonRepo
I need to add function pointers "all the way up" to the root component. -
I can create a record holding all the functions of my
PersonRepo
(and one for each abstraction I need to mock out). But I'm unsure if I then should create an explicit type e.g. for the record used inlookupPerson
the(Id;Status;Timestamp)
. -
Is there any other way? I prefer to keep the application functional.
an example module with side-effects I need to mock out during testing:
namespace PeanutCorp.Repositories
module PersonRepo =
let findPerson ssn =
use db = DbSchema.GetDataContext(ConnectionString)
query {
for ratingId in db.Rating do
where (Identifier.Identifier = ssn)
select (Some { Id = Identifier.Id; Status = Local; Timestamp = Identifier.LastChecked; })
headOrDefault
}
let savePerson id ssn timestamp status rating =
use db = DbSchema.GetDataContext(ConnectionString)
let entry = new DbSchema.Rating(Id = id,
Id = ClientId.Value,
Identifier = id,
LastChecked = timestamp,
Status = status,
Rating = rating
)
db.Person.InsertOnSubmit(entry)
...
let findCompany companyId = ...
let saveCompany id companyId timestamp status rating = ...
let findCachedPerson lookup identifier = ...
Aucun commentaire:
Enregistrer un commentaire