I'm building a Laravel 5 application at the moment and have gotten myself confused about how to mock things in PhpSpec.
I'm building a schedule times validator that requires the intended schedule to be checked against all current schedules and see if there's any overlap (events are not allowed to overlap).
I need to pull in the schedules in question so I can test against them. At the moment it's a very basic whereBetween query, but it's going to get a lot more complicated as there'll be recurring schedules to check against as well.
So here's my stripped down class. I really just want to test the doesNotOverlap function.
use App\Schedule;
class ScheduleTimesValidator
{
protected $schedule;
public function __construct(Schedule $schedule)
{
$this->schedule = $schedule;
}
public function doesNotOverlap($slug, $intended)
{
$schedules = $this->getSchedulesBetween($slug, $intended);
if(empty($schedules)) return true;
return false;
}
protected function getSchedulesBetween($slug, $intended)
{
// Casting to array to make testing a little easier
return $this->schedule->whereIsRecurring(false)
->ofChurch($slug)
->whereBetween('start', [$intended['start'], $intended['end']])
->get()->toArray();
}
and here's my Spec
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class ScheduleTimesValidatorSpec extends ObjectBehavior
{
protected $validIntended = [
'start' => '2015-12-01 12:00:00',
'end' => '2015-12-01 13:00:00'
];
protected $churchNonRecurringSchedules = [
['start' => '2014-11-20 13:00:00', 'end' => '2014-11-21 14:00:00'],
['start' => '2014-11-23 10:36:07', 'end' => '2014-11-23 11:36:07'],
];
function let($schedule)
{
$schedule->beADoubleOf('App\Schedule');
$this->beConstructedWith($schedule);
}
function it_is_initializable()
{
$this->shouldHaveType('App\Validation\ScheduleTimesValidator');
}
function it_should_return_true_if_it_does_not_overlap($schedule)
{
// $schedule->any()->willReturn([]);
// $schedule->whereIsRecurring()->shouldBeCalled();
// $schedule->whereIsRecurring(false)->ofChurch()->whereBetween()->get()->toArray()->willReturn([]);
// $schedule->willReturn([]);
// $this->getSchedulesBetween('slug', $this->validIntended)->willReturn([]);
$this->doesNotOverlap('slug', $this->validIntended)->shouldReturn(true);
}
// Tear Down
function letgo() {}
}
If I run it like that I get:
! it should return true if it does not overlap
method 'Double\App\Schedule\P8::whereIsRecurring()' not found.
I tried (as you can see) various commented out things to mock what $schedule will return, but that doesn't seem to work.
So I guess I want to mock the protected getSchedulesBetween
method in the class, but doing things like $this->getSchedulesBetween($arg, $arg)->willReturn(blah)
doesn't work.
Do I need to pull getSchedulesBetween() out of the class and move it into another class and then mock that? Or do I need to push $this->schedule->blah into the doestNotOverlap method so I can mock what $schedule will return?
I don't want to actually test the App\Schedule Laravel Model - I just want to mock what it's returning and will be hardcoding a variety of queries that will be run to get the different model results.
End of a long day here so brain a little zonked.
Update 2014-10-23
So I created a scope on my Schedule model
public function scopeSchedulesBetween($query, $slug, $intended)
{
return $query->whereIsRecurring(false)
->ofChurch($slug)
->whereBetween('start', [$intended['start'], $intended['end']]);
}
Then created a new App\Helpers\ScheduleQueryHelper which instantiated App\Schedule as a variable and added this method:
public function getSchedulesBetween($slug, $intended)
{
return $this->schedule->schedulesBetween($slug, $intended)->get()->toArray();
}
Then updated my spec to do
function let($scheduleQueryHelper)
{
$scheduleQueryHelper->beADoubleOf('App\Helpers\ScheduleQueryHelper');
$this->beConstructedWith($scheduleQueryHelper);
}
function it_should_return_true_if_it_does_not_overlap($scheduleQueryHelper)
{
$scheduleQueryHelper->getSchedulesBetween('slug', $this->validIntended)->willReturn([]);
$this->doesNotOverlap('slug', $this->validIntended)->shouldReturn(true);
}
And back in my ScheduleTimesValidator class did
public function doesNotOverlap($slug, $intended)
{
$schedules = $this->scheduleQueryHelper->getSchedulesBetween($slug, $intended);
if(empty($schedules)) {
return true;
}
return false;
}
And now PhpSpec is mocking that other class ok. However this seems like a very roundabout way to be doing things.