Methods which have not been mocked cannot have expectations
I'll go into a bit more detail below but this:
$mock = $this->getMockForTrait(EmployeeAttendenceTrait::class);
Is mocking no methods, as such $mock is the equivalent of:
class Testing extends PHPUnitsInternalMockStub
{
use EmployeeAttendenceTrait;
}
$mock = new Testing;
Check the error message
The error message reads:
Trying to configure method getOfficeDays which cannot be configured because it does not exist, has not been specified, is final, or is static
Let's take a look at each possibility (almost in turn)
It does not exist
The code looks to clearly have a method named getOfficeDays - if in doubt using get_class_methods would clarify:
<?php
use PHPUnit\Framework\TestCase;
class SOTest extends TestCase
{
public function testMethods()
{
$mock = $this->getMockForTrait(EmployeeAttendenceTrait::class);
print_r(get_class_methods($mock));
}
}
Which outputs:
R 1 / 1 (100%)Array
(
[0] => __clone
[1] => expects
[2] => method
[3] => __phpunit_setOriginalObject
[4] => __phpunit_getInvocationMocker
[5] => __phpunit_hasMatchers
[6] => __phpunit_verify
[7] => isLate
[8] => getOfficeDays
)
Time: 569 ms, Memory: 12.00MB
There was 1 risky test:
1) SOTest::testMethods
This test did not perform any assertions
So it's not that.
is final
public function getOfficeDays(): array {
The method signature clearly does not declare the method as final - so it's not that.
or is static
public function getOfficeDays(): array {
The method signature clearly is also not static - so it's not that either.
has not been specified
By logical deduction this has to be the problem - and it's the only one which requires some understanding of using phpunit.
The docs for getMockForTrait read (emphasis added):
The getMockForTrait() method returns a mock object that uses a specified trait. All abstract methods of the given trait are mocked. This allows for testing the concrete methods of a trait.
Since none of the trait methods are abstract, no methods are expected to be mocked with the syntax in the question. Looking deeper, the method signature for getMockForTrait has more arguments:
/**
* Returns a mock object for the specified trait with all abstract methods
* of the trait mocked. Concrete methods to mock can be specified with the
* `$mockedMethods` parameter.
*
* @throws RuntimeException
*/
public function getMockForTrait(
string $traitName,
array $arguments = [],
string $mockClassName = '',
bool $callOriginalConstructor = true,
bool $callOriginalClone = true,
bool $callAutoload = true,
array $mockedMethods = [],
bool $cloneArguments = true): MockObject
There is a parameter for specifying explicitly mocked Methods, therefore writing the test like so:
class SOTest extends TestCase
{
public function testMethods()
{
$mock = $this->getMockForTrait(
EmployeeAttendenceTrait::class,
[], # These
'', # are
true, # the
true, # defaults
true, #
['getOfficeDays']
);
$mock->expects($this->once())
->method('getOfficeDays')
->willReturn([]);
$today = new DateTime(); // or whatever is appropriate
$this->assertTrue($mock->isLate($today));
}
}
Will generate a valid mock object and allow the test to run.