PHP单元测试 file_get_contents
PHPUnit testing file_get_contents
我有一个 class,它有一个使用 PHP 的全局 file_get_contents
函数的方法。我需要在 class 上测试方法,而不实际调用全局函数。
我知道我可以使用命名空间来覆盖从 file_get_contents
返回的内容,但是我的测试已经在一个单独的命名空间中,所以我不能简单地将命名空间与 class.[=16 匹配=]
这是一些代码:
class
<?php
namespace MyVendor\MyProject;
class MyClass
{
private $someProperty;
public function __construct($override = '')
{
$this->someProperty = $override;
}
public function myMethod()
{
$request = 'http://example.com';
$response = $this->someMethodUsingGlobals($request);
// Do something with the response..
}
public function someMethodUsingGlobals($url)
{
return json_decode(file_get_contents($url),true)['results'][0];
}
}
测试
<?php
namespace MyProjectTests;
public function test_it_does_something_with_the_response()
{
$sut = new MyClass();
$response = $sut->myMethod();
$this->assertEquals('Some expectation', $response);
}
我需要在 class 上模拟 someMethodUsingGlobals()
方法,但不完全确定如何去做。
您可以使用部分模拟对象对其进行存档:您可以仅模拟 class 的特定方法并执行(和测试)其他方法。
进一步参考 here
例如,假设您修改了 class:
<?php
namespace Acme\DemoBundle\Service;
class MyClass {
public function myMethod()
{
$request = 'http://domain.com';
$response = $this->someMethodUsingGlobals($request);
// Do something with the response..
return $response;
}
public function someMethodUsingGlobals($url)
{
return json_decode(file_get_contents($url),true)['results'][0];
}
}
你可以用下面的测试来测试class:
<?php
namespace Acme\DemoBundle\Tests;
class MyProjectTest extends \PHPUnit_Framework_TestCase
{
public function test_it_does_something_with_the_response()
{
$sut = $this->getMock('Acme\DemoBundle\Service\MyClass', array('someMethodUsingGlobals') );
// Set up the expectation for the someMethodUsingGlobals() method
// to be called only once and with the string 'http://domain.com'
// as its parameter.
$sut->expects($this->once())
->method('someMethodUsingGlobals')
->with($this->equalTo('http://domain.com'))
->willReturn('Some expectation');
$response = $sut->myMethod();
$this->assertEquals('Some expectation', $response);
}
}
因此方法 someMethodUsingGlobals
未执行,并且 return 模拟定义中定义的值。使用模拟函数执行和处理方法 myMethod
。
希望对您有所帮助
解决方案:对原生函数进行 class-包装
最简单、最干净的方法是围绕本机函数创建一个包装器 class。
如果你遵循 DDD and/or 六边形架构,你可能会把它放在 "adapters" space 中,如果你不遵循 DDD 或 hex-arch,将它放在 "adapters" space 中除了 class 的任何一组 "touch the exterior world"。
这个包装纸是单层的class:
<?php
declare( strict_types = 1 );
namespace MyVendor\MyProject\Adapters;
class FileGetContentsWrapper
{
public function fileGetContents( string $filename )
{
return file_get_contents( $filename );
}
}
这个class无法测试,因为它只是使用了原生函数。
但是有了它,您只需 "shift" 将 "untesting" 添加到这个单行 class 并且您现在可以将所有其他曾经使用 file_get_contents()
的地方可测试的覆盖除了文件读取之外的代码周围的逻辑。
你原来的class,修改过的
你这样进行:
- 您通过构造函数注入服务。
- 您将包装器视为一项服务。
- 如果您在最新版本中使用像 symfony 这样的框架,您可以使用自动连接进行注入以简化 class.
的构造
例如,您的 class 可能会导致:
<?php
namespace MyVendor\MyProject;
use MyVendor\MyProject\Adapters\FileGetContentsWrapper;
class MyClass
{
private $fileGetContentsWrapper;
public function __construct( FileGetContentsWrapper $fileGetContentsWrapper )
{
$this->fileGetContentsWrapper = $fileGetContentsWrapper;
}
/* ... */
public function someMethodUsingTheWrapper( $url )
{
$contents = $this->fileGetContents( $url );
return json_decode( $contents, true )[ 'results' ][ 0 ];
}
}
如何测试
您执行以下操作:
- 在测试 class 中,您为包装器设置了一个模拟。
- 在测试方法中,您可以根据需要将模拟配置为 return。
- 您正常调用 class。
例如:
<?php
declare( strict_types = 1 );
namespace MyProjectTests;
use MyVendor\MyProject\Adapters\FileGetContentsWrapper;
use MyVendor\MyProject\MyClass;
use PHPUnit\Framework\TestCase;
class MyClassTest extends TestCase
{
private $fileGetContentsWrapper;
//---------------------------------------------------------------------//
// Setup //
//---------------------------------------------------------------------//
protected function setUp()
{
$this->fileGetContentsWrapper = $this->createMock( FileGetContentsWrapper::class )
parent::setUp();
}
//---------------------------------------------------------------------//
// Tests //
//---------------------------------------------------------------------//
public function testSomeMethodUsingTheWrapper()
{
$sut = $this->getSut();
$someSimulatedJson = '{"results":["abc","xyz"]}';
$this->fileGetContentsWrapper->method( 'fileGetContents' )->willReturn( $someSimulatedJson );
$this->assertEquals( 'xyz', $sut->someMethodUsingGlobals( 'dummy-url' ) );
}
//---------------------------------------------------------------------//
// Private //
//---------------------------------------------------------------------//
private function getSut() : MyClass
{
return new MyClass( $this->fileGetContentsWrapper );
}
}
就是这样!希望能帮到你!
我有一个 class,它有一个使用 PHP 的全局 file_get_contents
函数的方法。我需要在 class 上测试方法,而不实际调用全局函数。
我知道我可以使用命名空间来覆盖从 file_get_contents
返回的内容,但是我的测试已经在一个单独的命名空间中,所以我不能简单地将命名空间与 class.[=16 匹配=]
这是一些代码:
class
<?php
namespace MyVendor\MyProject;
class MyClass
{
private $someProperty;
public function __construct($override = '')
{
$this->someProperty = $override;
}
public function myMethod()
{
$request = 'http://example.com';
$response = $this->someMethodUsingGlobals($request);
// Do something with the response..
}
public function someMethodUsingGlobals($url)
{
return json_decode(file_get_contents($url),true)['results'][0];
}
}
测试
<?php
namespace MyProjectTests;
public function test_it_does_something_with_the_response()
{
$sut = new MyClass();
$response = $sut->myMethod();
$this->assertEquals('Some expectation', $response);
}
我需要在 class 上模拟 someMethodUsingGlobals()
方法,但不完全确定如何去做。
您可以使用部分模拟对象对其进行存档:您可以仅模拟 class 的特定方法并执行(和测试)其他方法。
进一步参考 here
例如,假设您修改了 class:
<?php
namespace Acme\DemoBundle\Service;
class MyClass {
public function myMethod()
{
$request = 'http://domain.com';
$response = $this->someMethodUsingGlobals($request);
// Do something with the response..
return $response;
}
public function someMethodUsingGlobals($url)
{
return json_decode(file_get_contents($url),true)['results'][0];
}
}
你可以用下面的测试来测试class:
<?php
namespace Acme\DemoBundle\Tests;
class MyProjectTest extends \PHPUnit_Framework_TestCase
{
public function test_it_does_something_with_the_response()
{
$sut = $this->getMock('Acme\DemoBundle\Service\MyClass', array('someMethodUsingGlobals') );
// Set up the expectation for the someMethodUsingGlobals() method
// to be called only once and with the string 'http://domain.com'
// as its parameter.
$sut->expects($this->once())
->method('someMethodUsingGlobals')
->with($this->equalTo('http://domain.com'))
->willReturn('Some expectation');
$response = $sut->myMethod();
$this->assertEquals('Some expectation', $response);
}
}
因此方法 someMethodUsingGlobals
未执行,并且 return 模拟定义中定义的值。使用模拟函数执行和处理方法 myMethod
。
希望对您有所帮助
解决方案:对原生函数进行 class-包装
最简单、最干净的方法是围绕本机函数创建一个包装器 class。
如果你遵循 DDD and/or 六边形架构,你可能会把它放在 "adapters" space 中,如果你不遵循 DDD 或 hex-arch,将它放在 "adapters" space 中除了 class 的任何一组 "touch the exterior world"。
这个包装纸是单层的class:
<?php
declare( strict_types = 1 );
namespace MyVendor\MyProject\Adapters;
class FileGetContentsWrapper
{
public function fileGetContents( string $filename )
{
return file_get_contents( $filename );
}
}
这个class无法测试,因为它只是使用了原生函数。
但是有了它,您只需 "shift" 将 "untesting" 添加到这个单行 class 并且您现在可以将所有其他曾经使用 file_get_contents()
的地方可测试的覆盖除了文件读取之外的代码周围的逻辑。
你原来的class,修改过的
你这样进行:
- 您通过构造函数注入服务。
- 您将包装器视为一项服务。
- 如果您在最新版本中使用像 symfony 这样的框架,您可以使用自动连接进行注入以简化 class. 的构造
例如,您的 class 可能会导致:
<?php
namespace MyVendor\MyProject;
use MyVendor\MyProject\Adapters\FileGetContentsWrapper;
class MyClass
{
private $fileGetContentsWrapper;
public function __construct( FileGetContentsWrapper $fileGetContentsWrapper )
{
$this->fileGetContentsWrapper = $fileGetContentsWrapper;
}
/* ... */
public function someMethodUsingTheWrapper( $url )
{
$contents = $this->fileGetContents( $url );
return json_decode( $contents, true )[ 'results' ][ 0 ];
}
}
如何测试
您执行以下操作:
- 在测试 class 中,您为包装器设置了一个模拟。
- 在测试方法中,您可以根据需要将模拟配置为 return。
- 您正常调用 class。
例如:
<?php
declare( strict_types = 1 );
namespace MyProjectTests;
use MyVendor\MyProject\Adapters\FileGetContentsWrapper;
use MyVendor\MyProject\MyClass;
use PHPUnit\Framework\TestCase;
class MyClassTest extends TestCase
{
private $fileGetContentsWrapper;
//---------------------------------------------------------------------//
// Setup //
//---------------------------------------------------------------------//
protected function setUp()
{
$this->fileGetContentsWrapper = $this->createMock( FileGetContentsWrapper::class )
parent::setUp();
}
//---------------------------------------------------------------------//
// Tests //
//---------------------------------------------------------------------//
public function testSomeMethodUsingTheWrapper()
{
$sut = $this->getSut();
$someSimulatedJson = '{"results":["abc","xyz"]}';
$this->fileGetContentsWrapper->method( 'fileGetContents' )->willReturn( $someSimulatedJson );
$this->assertEquals( 'xyz', $sut->someMethodUsingGlobals( 'dummy-url' ) );
}
//---------------------------------------------------------------------//
// Private //
//---------------------------------------------------------------------//
private function getSut() : MyClass
{
return new MyClass( $this->fileGetContentsWrapper );
}
}
就是这样!希望能帮到你!