php-mock-mockery icon indicating copy to clipboard operation
php-mock-mockery copied to clipboard

(Using Pest) If the first test doesn't use PHPMockery, but subsequent tests do, they don't work

Open Gerben-T opened this issue 9 months ago • 1 comments

Pest 3.7.4 Mockery 1.6.12 php-mock-mockery 1.5.0

Given the following test file test.php

<?php

namespace TEST;

use Mockery;

class ABC {
	function test() {
		$result = json_encode([]);
		if($result === false) {
			return false;
		}
		return $result;
	}
}

afterEach(function () {
	Mockery::close();
});

it('should return the json encoded array', function () {
	$abc = new ABC();
	$result = $abc->test();
	expect($result)->toBe('[]');
});

it('should return false when json_encode fails', function () {
	$abc = new ABC();

	\phpmock\mockery\PHPMockery::mock(__NAMESPACE__, 'json_encode')
		->andReturn(false);

	$result = $abc->test();
	expect($result)->toBe(false);
});

And running using

 php vendor/pestphp/pest/bin/pest test.php

You'll notice that the second test fails with

 Failed asserting that '[]' is identical to false.

However when you run

 php vendor/pestphp/pest/bin/pest test.php --filter "/should return false when json_encode fails/"

it succeeds

✓ it should return false when json_encode fails

I've done some testing and this happens only when the first test doesn't use PHPMockery::mock().

This setup works

<?php

namespace TEST;

use Mockery;

class ABC {
	function test() {
		$result = json_encode([]);
		if($result === false) {
			return false;
		}
		return $result;
	}
}

afterEach(function () {
	Mockery::close();
});

it('should return false when json_encode fails', function () {
	$abc = new ABC();

	\phpmock\mockery\PHPMockery::mock(__NAMESPACE__, 'json_encode')
		->andReturn(false);

	$result = $abc->test();
	expect($result)->toBe(false);
});

it('should return the json encoded array', function () {
	$abc = new ABC();
	$result = $abc->test();
	expect($result)->toBe('[]');
});

and it also doesn't matter if there are other tests after that that use PHPMockery::mock()

<?php

namespace TEST;

use Mockery;

class ABC {
	function test() {
		$result = json_encode([]);
		if($result === false || $result === 'somethingunique') {
			return false;
		}
		return $result;
	}
}

afterEach(function () {
	Mockery::close();
});

it('should return false when json_encode fails', function () {
	$abc = new ABC();

	\phpmock\mockery\PHPMockery::mock(__NAMESPACE__, 'json_encode')
		->andReturn(false);

	$result = $abc->test();
	expect($result)->toBe(false);
});

it('should return the json encoded array', function () {
	$abc = new ABC();
	$result = $abc->test();
	expect($result)->toBe('[]');
});

it('should return false when json_encode returns something unique', function () {
	$abc = new ABC();

	\phpmock\mockery\PHPMockery::mock(__NAMESPACE__, 'json_encode')
		->andReturn('somethingunique');

	$result = $abc->test();
	expect($result)->toBe(false);
});

What could be the problem here?

Gerben-T avatar Jul 10 '25 12:07 Gerben-T

I care about the order my tests are in so reordering them just for this to work (and adding a comment somewhere about this problem) is not acceptable.

I tried some things and noticed that if i use define before starting the test it works

beforeEach(function () {
	\phpmock\mockery\PHPMockery::define(__NAMESPACE__, 'json_encode');
});

Could this be related to Bug #68541?

Gerben-T avatar Jul 10 '25 13:07 Gerben-T