提问人:Meyer Auslander - Tst 提问时间:6/15/2023 更新时间:6/15/2023 访问量:52
在多个命名空间中定义相同的 PHP 函数
Define the same PHP function in many namespaces
问:
为了创建用于单元测试的全局函数的模拟版本,我在 SUT 类的命名空间中定义其模拟版本,如此处所述
考虑在命名空间和命名空间中使用的全局函数的情况。如果我想在两个地方使用相同的模拟版本,似乎我不得不对foo()
Bar
Baz
foo()
foo()
/* bar-namespace-mocks.php */
namespace Bar;
function foo(){
return 'foo mock';
}
/* baz-namespace-mocks.php */
namespace Baz;
function foo(){
return 'foo mock';
}
此代码不符合 DRY 主体。 最好有一个 foo mock 的声明
/* foo-mock.php */
function foo(){
return 'foo mock';
}
然后根据需要导入到每个命名空间中,如以下伪代码所示:
/* namespace-mocks.php */
namespace Bar{
import 'foo-mock.php';
}
namespace Baz{
import 'foo-mock.php';
}
使用 include 导入,例如
namespace Baz;
include 'foo-mock.php'
不会导致在命名空间中声明模拟。有没有办法在多个命名空间中声明一个函数,而没有多个版本?Baz
答:
如果你需要抽象出一个原生函数,那么就为它做一个契约,这样你就可以对它提供的服务使用依赖注入:
interface FooInterface
{
public function get(): string;
}
然后提供使用本机函数的默认实现:
class NativeFoo implements FooInterface
{
public function get(): string
{
return \foo();
}
}
然后,您的主类可能如下所示:
class A
{
protected FooInterface $foo;
public function __construct(FooInterface $foo = null)
{
$this->foo = $foo ?? new NativeFoo();
}
public function whatever()
{
$foo = $this->foo->get();
}
}
因此,您在构造函数中有一个可选参数,如果您不提供值,您将获得本机 foo 函数作为默认值。你只需要做:
$a = new A();
$a->whatever();
但是,如果您想覆盖它,您可以执行以下操作:
$foo = new SomeOtherFoo();
$a = new A($foo);
$a->whatever();
然后,在测试中,可以构建一个显式模拟:
class MockFoo implements FooInterface
{
public function get(): string
{
return 'some test value';
}
}
并传递实例:
$foo = new MockFoo();
$a = new A($foo);
$a->whatever();
或者使用 PHPUnit 模拟构建器:
$foo = $this->getMockBuilder(FooInterface::class)->... // whatever
如果你想要一个更简单的版本,你可以只使用一个可调用的:
class A
{
protected $foo;
public function __construct(callable $foo = null)
{
$this->foo = $foo ?? 'foo';
}
public function whatever()
{
$foo = call_user_func($this->foo);
}
}
// default usage
$a = new A();
$a->whatever();
// test usage
$a = new A(fn() => 'some other value');
$a->whatever();
评论
foo
$foo
$a = new A('fooOverrideFunction');
$foo
虽然我完全同意 Alex Howansky 的回答,但如果你真的一心想使用简单的函数,那么你总是可以:
namespace Mocks {
function foo() {
return 'foo';
}
}
namespace Bar {
function foo() {
return \Mocks\foo();
}
}
namespace Baz {
function foo() {
return \Mocks\foo();
}
}
namespace asdf {
var_dump(\Bar\foo(), \Baz\foo());
}
输出:
string(3) "foo"
string(3) "foo"
虽然在这一点上,我们真的只是在重塑类的路上,但更糟。在某些时候,你会想知道如何命名一个变量,这根本不可能,所以你会在这一点上将其滚动到类中。
类、接口和特征/继承将最恰当地利用来解决这个问题。
评论
Mocks
foo_mock()
foo()
foo_mock()
上一个:Twig 可选命名空间模板
下一个:命名空间与 phpUnit 冲突
评论