单元测试中dataProvider函数工作异常问题排查
待测试的Handle函数
public function handle(Request $request, Closure $next): mixed { $fields = [ 'address1', 'address2', 'address3', 'city', 'country', 'countryName', 'email', 'firstname', 'lastname', 'mobile', 'phone', 'postcode', 'state', 'title' ]; foreach ($fields as $field) { if ($request->get($field) !== null) { return $this->scaActionMiddleware->handle($request, $next); } } /** @var Response $response */ $response = $next($request); return $response; }
原测试代码
/** * @dataProvider dataProvider */ public function testHandleShouldBeOk(string $input, string $value): void { $request = $this->createMock(Request::class); $response = $this->createMock(JsonResponse::class); $next = (function () use ($response) { return $response; })(...); $request->expects(self::any()) ->method('get') ->with($input) ->willReturn($value); $this->scaActionMiddleware->expects(self::once()) ->method('handle') ->with($request, $next); $this->scaActionUserMiddleware->handle($request, $next); } /** * @return string[][] */ private function dataProvider(): array { return [ [ 'address1', '80 boulevard magenta' ], [ 'address2', '22 rue du Commerce' ], [ 'address3', '124 avenue des Champs' ], [ 'city', 'Paris' ], [ 'country', 'FR' ], [ 'countryName', 'France' ], [ 'email', 'my-mail@email.com' ], [ 'firstname', 'Paul' ], [ 'lastname', 'Bar' ], [ 'mobile', '0601589540' ], [ 'postcode', '75010' ], [ 'state', 'state' ], [ 'address1', 'address1' ], [ 'title', 'Mr.' ] ]; }
测试报错信息
Expectation failed for method name is "get" when invoked zero or more
times Parameter 0 for invocation
Illuminate\Http\Request::get('address1', null): mixed does not match
expected value. Failed asserting that two strings are equal. Expected
:'address2' Actual :'address1'
Expectation failed for method name is "get" when invoked zero or more
times Parameter 0 for invocation
Illuminate\Http\Request::get('address1', null): mixed does not match
expected value. Failed asserting that two strings are equal. Expected
:'address3' Actual :'address1'
问题原因
原测试使用Request Mock时,给get方法设置了仅接收指定参数$input的断言,但待测试的handle函数会循环遍历所有字段,依次调用$request->get($field)。比如测试第二个用例(address2)时,代码会先调用get('address1'),而Mock的get方法只期望接收address2,导致参数匹配失败,触发报错。
解决方法
停止使用Request Mock,改用真实的Request对象,这样get方法能正确返回对应字段的值,不会出现参数匹配限制。修改后的测试代码如下:
/** * @dataProvider dataProvider * * @throws ConnectDbAccessException * @throws JwtServiceException * @throws ConfigServiceException * @throws ScaServiceException * @throws SerializerServiceException */ public function testHandleShouldBeOk(string $field, string $value, int $invoke): void { $request = Request::create('foo', 'POST', [$field => $value]); $next = function () { return $this->createMock(Response::class); }; $this->scaActionMiddleware->expects(self::exactly($invoke)) ->method('handle') ->with($request, $next); $this->scaActionUserMiddleware->handle($request, $next); } /** * @return array<int, array<int, int|string>> */ private function dataProvider(): array { return [ ['address1', '80 boulevard magenta', 1], ['address2', '22 rue du Commerce', 1], ['address3', '124 avenue des Champs', 1], ['city', 'Paris', 1], ['country', 'FR', 1], ['countryName', 'France', 1], ['email', 'my-mail@email.com', 1], ['firstname', 'Paul', 1], ['lastname', 'Bar', 1], ['mobile', '0601589540', 1], ['postcode', '75010', 1], ['state', 'state', 1], ['address1', 'address1', 1], ['title', 'Mr.', 1], ['foo', '80 boulevard magenta', 0], ]; }
内容的提问来源于stack exchange,提问作者Mitsukk

