views:

156

answers:

3

I'd like to write F# unit test with mock objects. I'm using NUnit. But unfortunately I couldn't find any examples.

Here's an example of the code under test:

type ICustomer = interface
    abstract Id: int with get
    abstract Name: string with get
    abstract CalculateBalanceWithDiscount: decimal -> decimal
end

type Customer = class
    val id: int
    val name: string
    val balance: decimal
    new(id, name, balance) = 
        {id = id; name = name; balance = balance}
    interface ICustomer with
        member this.Id 
            with get () = this.id
        member this.Name
            with get () = this.name
        member this.CalculateBalanceWithDiscount discount =
            this.balance - (discount * this.balance)
    end
end
A: 

type ICustomer = interface abstract Id: int with get abstract Name: string with get abstract CalculateBalanceWithDiscount: decimal -> decimal end

type Customer = class
    val id: int
    val name: string
    val balance: decimal
    new(id, name, balance) = 
        {id = id; name = name; balance = balance}
    interface ICustomer with
        member this.Id 
            with get () = this.id
        member this.Name
            with get () = this.name
        member this.CalculateBalanceWithDiscount discount =
            this.balance - (discount * this.balance)
    end
end
Anastasia
+2  A: 

As a side-note, you can use implicit constructor syntax to make your class declaration a bit nicer. You can also simplify readonly properties, because you can omit with get():

// F# infers that the type is an interface
type ICustomer = 
  abstract Id : int 
  abstract Name : string 
  abstract CalculateBalanceWithDiscount : decimal -> decimal

// Parameters of the implicit constructor are autoamtically
// accessible in the body (they are stored as fields)
type Customer(id:int, name:string, balance:decimal) = 
  interface ICustomer with
    member this.Id = id
    member this.Name = name
    member this.CalculateBalanceWithDiscount(discount) =
      balance - (discount * balance)

Regarding testing - do you have any example of what you're trying to achieve? I'm sure we can help for example with translating code from C#. Or what kind of tests would you like to write using mocking?

In general, a nice thing about F# and functional languages is that you can usually test code more easily without using any mocks. Functional programs are written in a different style:

In functional programming, a function takes all it's inputs as arguments and the only thing that it does is that it calculates and returns some result. This is also true for methods of immutable object types - they do not modify any state of any objects

Mocks are typically used for two purposes:

  • To verify that the tested operation performed some call to a method of a referenced object e.g. prod.Update(newPrice) to update the state of the object. However, in functional programming the method should instead return the new state as the result - so you don't need mock object. Just check whether the new returned state is what you expected.

  • To load create a fake component of the application, for example instead of loading data from the database. Again, a purely functional function should take all it's inputs as arguments. This means that you don't need to create a mock object - you just call the function with some test data as argument (instead of data loaded from database).

In summary, this means that in a well-designed functional program, you should be able to write all unit tests simply as checks that verify that some function returns the expected result for the expected arguments. Of course, this isn't strictly true in F#, because you may need to interoperate with other impure .NET components (but that can be answered only if you give a more specific example).

Tomas Petricek
+1  A: 

You don't need to create a class in order to create mocks:

/// customer : int -> string -> decimal -> ICustomer
let customer id name balance = 
    {new ICustomer with
        member this.Id = id
        member this.Name = name
        member this.CalculateBalanceWithDiscount discount =
            balance - (discount * balance) }
forki23