Test test calculate pay mockEmployee := mock(IEmployee) employee := Employee( mockEmployee ) startDate := Date(1, July, 2006) endDate := Date(31, July, 2006) expect once ( mockEmployee.labour(startDate, endDate) ) will returnValue( 40 ) expect once ( mockEmployee.payRate() ) will returnValue( 48 ) expect once ( mockEmployee.premium() ) will returnValue( 2) assertEqual( 2000, employee.calculatePay(startDate, endDate) )with an implementation like this:
Employee( target ) implements IEmployee this.target := target Employee.calculatePay( startDate, endDate ) labour := target.labour(startDate, endDate) rate := target.payRate() premium := target.premium() return labour * (rate + premium)Incidentally, both the real and mock Employees implement the IEmployee interface. The problems I see with this example are:
- the Employee class isn't complicated enough to mock. It's basically a struct with a helper method. We don't usually mock simple classes that have no interactions with third parties
- the record/playback style of mocking is too brittle. It's OK for the simple cases and to learn the approach, but we want to think in terms of expressing constraints between objects, not just simple matching.
- everything's a getter. Mocks should push you towards a "Tell, Don't Ask" style of coding, where behaviour is passed around rather than data.
- I find the double implementation of IEmployee confusing. The test doesn't really help to flush out the relationship between an Employee object and its neighbours
Test test reports pay to payroll timesheet := Timesheet() .set( Week(3), Hours(40) ) employee := Employee( HourlyRate(50), timesheet ) payroll := mock(Payroll) expect once (payroll.addEmployeeForWeek( employee, Week(3), Rate(2000) ) employee.reportPayForWeekTo( Week(3), payroll ) Employee( hourlyRate, timesheet ) this.hourlyRate := hourlyRate this.timesheet = timesheet Employee.reportPayForWeekTo( week, payroll ) payroll.addEmployeeForWeek(this, week, hourlyRate.valueOf( timesheet.hoursForWeek(week) ))Note that now the expectation doesn't have to return anything because we're sending a message to the payroll object. Also, more of the objects have behaviour on them and the employee has a more meaningful relationship with its neighbour than before.