Skip to content

Commit

Permalink
Include mock expectations in assertion count (#6672)
Browse files Browse the repository at this point in the history
* Add failing test for including mock expectations in assertion count

* Use PHPUnit's assertion count for tests wrapped by the TestCaseWrapper

This fixes the problem that the mock expectations count was not included
 in the assertion count.
  • Loading branch information
rene-bos committed Jun 6, 2023
1 parent f1a95e4 commit 237630e
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 18 deletions.
2 changes: 2 additions & 0 deletions composer.json
Expand Up @@ -90,6 +90,8 @@
"tests/data/register_command/examples",
"tests/data/DummyClass.php",
"tests/data/DummyOverloadableClass.php",
"tests/data/services/UserModel.php",
"tests/data/services/UserService.php",
"tests/unit/Codeception/Command/BaseCommandRunner.php",
"tests/unit/Codeception/Util/MockAutoload.php",
"tests/unit/Codeception/Util/ReflectionTestClass.php"
Expand Down
37 changes: 22 additions & 15 deletions src/Codeception/Test/Test.php
Expand Up @@ -56,8 +56,6 @@ abstract class Test extends TestWrapper implements TestInterface, Interfaces\Des

private bool $ignored = false;

private int $assertionCount = 0;

private ?EventDispatcher $eventDispatcher = null;

/**
Expand Down Expand Up @@ -208,12 +206,16 @@ final public function realRun(ResultAggregator $result): void
}

$time = $timer->stop()->asSeconds();
$this->assertionCount = Assert::getCount();
$result->addToAssertionCount($this->assertionCount);

$this->callTestEndHooks($status, $time, $e);

// We need to get the number of performed assertions _after_ calling the test end hooks because the
// AssertionCounter needs to set the number of performed assertions first.
$result->addToAssertionCount($this->numberOfAssertionsPerformed());

if (
$this->reportUselessTests &&
$this->assertionCount === 0 &&
$this->numberOfAssertionsPerformed() === 0 &&
!$this->doesNotPerformAssertions() &&
$eventType === Events::TEST_SUCCESS
) {
Expand All @@ -228,15 +230,8 @@ final public function realRun(ResultAggregator $result): void
} else {
$this->fire($eventType, new FailEvent($this, $e, $time));
}
}

foreach (array_reverse($this->hooks) as $hook) {
if ($hook === 'codeCoverage' && !$this->collectCodeCoverage) {
continue;
}
if (method_exists($this, $hook . 'End')) {
$this->{$hook . 'End'}($status, $time, $e);
}
} else {
$this->callTestEndHooks($status, $time, $e);
}

$this->fire(Events::TEST_AFTER, new TestEvent($this, $time));
Expand Down Expand Up @@ -278,7 +273,7 @@ protected function ignore(bool $ignored): void

public function numberOfAssertionsPerformed(): int
{
return $this->assertionCount;
return $this->getNumAssertions();
}


Expand All @@ -295,4 +290,16 @@ protected function fire(string $eventType, TestEvent $event): void
}
$this->eventDispatcher->dispatch($event, $eventType);
}

private function callTestEndHooks(string $status, float $time, ?Throwable $e): void
{
foreach (array_reverse($this->hooks) as $hook) {
if ($hook === 'codeCoverage' && !$this->collectCodeCoverage) {
continue;
}
if (method_exists($this, $hook . 'End')) {
$this->{$hook . 'End'}($status, $time, $e);
}
}
}
}
20 changes: 19 additions & 1 deletion src/Codeception/Test/TestCaseWrapper.php
Expand Up @@ -147,13 +147,15 @@ public function test(): void
$this->testCase->setDependencyInput($dependencyInput);
$this->testCase->runBare();

$this->testCase->addToAssertionCount(Assert::getCount());

if (PHPUnitVersion::series() < 10) {
self::$testResults[$this->getSignature()] = $this->testCase->getResult();
} else {
self::$testResults[$this->getSignature()] = $this->testCase->result();
}

$numberOfAssertionsPerformed = Assert::getCount();
$numberOfAssertionsPerformed = $this->getNumAssertions();
if (
$this->reportUselessTests &&
$numberOfAssertionsPerformed > 0 &&
Expand Down Expand Up @@ -200,4 +202,20 @@ private function getNameWithDataSet(): string

return $this->testCase->nameWithDataSet();
}

/**
* Override this method from the {@see \Codeception\Test\Feature\AssertionCounter} so that we use PHPUnit's
* assertion count instead of our own.
* This is needed because PHPUnit's {@see TestCase} has a {@see TestCase::addToAssertionCount()} method which is
* both internally and externally used to increase the assertion count. Externally it is called from tearDown
* methods, for example when using Mockery.
*/
public function getNumAssertions(): int
{
if (PHPUnitVersion::series() < 10) {
return $this->testCase->getNumAssertions();
} else {
return $this->testCase->numberOfAssertionsPerformed();
}
}
}
7 changes: 5 additions & 2 deletions tests/cli/RunUselessTestsCest.php
Expand Up @@ -92,30 +92,33 @@ public function checkReports(CliGuy $I): void
$I->seeInShellOutput('UselessTest: Make no assertions............................................Useless');
$I->seeInShellOutput('UselessTest: Expects not to perform assertions.............................Ok');
$I->seeInShellOutput('UselessTest: Make unexpected assertion.....................................Useless');
$I->seeInShellOutput('UselessTest: Mock expectations.............................................Ok');

$I->seeInShellOutput('JUNIT XML report generated in');
$I->seeInShellOutput('PHPUNIT XML report generated in');
$I->seeInShellOutput('HTML report generated in');
$I->seeFileFound('report.xml', 'tests/_output');
$I->seeInThisFile(
'<testsuite name="unit" tests="5" assertions="1" errors="0" failures="0" skipped="0" useless="4" time="'
'<testsuite name="unit" tests="6" assertions="2" errors="0" failures="0" skipped="0" useless="4" time="'
);
$I->seeInThisFile('<testcase name="Useless"');
$I->seeInThisFile('<testcase name="makeNoAssertions" class="UselessCest"');
$I->seeInThisFile('<testcase name="testMakeNoAssertions" class="UselessTest" file="');
$I->seeInThisFile('<testcase name="testExpectsNotToPerformAssertions" class="UselessTest" file="');
$I->seeInThisFile('<testcase name="testMakeUnexpectedAssertion" class="UselessTest" file="');
$I->seeInThisFile('<testcase name="testMockExpectations" class="UselessTest" file="');
$I->seeInThisFile('<error>Useless Test</error>');

$I->seeFileFound('phpunit-report.xml', 'tests/_output');
$I->seeInThisFile(
'<testsuite name="unit" tests="5" assertions="1" errors="0" failures="0" skipped="0" useless="4" time="'
'<testsuite name="unit" tests="6" assertions="2" errors="0" failures="0" skipped="0" useless="4" time="'
);
$I->seeInThisFile('<testcase name="Useless"');
$I->seeInThisFile('<testcase name="makeNoAssertions" class="UselessCest"');
$I->seeInThisFile('<testcase name="testMakeNoAssertions" class="UselessTest" file="');
$I->seeInThisFile('<testcase name="testExpectsNotToPerformAssertions" class="UselessTest" file="');
$I->seeInThisFile('<testcase name="testMakeUnexpectedAssertion" class="UselessTest" file="');
$I->seeInThisFile('<testcase name="testMockExpectations" class="UselessTest" file="');
$I->seeInThisFile('<error>Useless Test</error>');

$I->seeFileFound('report.html', 'tests/_output');
Expand Down
15 changes: 15 additions & 0 deletions tests/data/useless/tests/unit/UselessTest.php
@@ -1,5 +1,7 @@
<?php

use Codeception\Stub\Expected;

class UselessTest extends \Codeception\Test\Unit
{
/**
Expand All @@ -21,4 +23,17 @@ public function testMakeUnexpectedAssertion(): void
$this->expectNotToPerformAssertions();
$this->assertTrue(true);
}

public function testMockExpectations(): void
{
$user = $this->make(
UserModel::class,
[
'setName' => Expected::once('Foo'),
],
);

$userService = new UserService($user);
$userService->create('Foo');
}
}

0 comments on commit 237630e

Please sign in to comment.