Php 未调用Laravel部分模拟模型

Php 未调用Laravel部分模拟模型,php,unit-testing,laravel,mockery,partial-mocks,Php,Unit Testing,Laravel,Mockery,Partial Mocks,我正在使用laravel(4.2)框架开发一个web应用程序(PHP5.4.25)。我创建了一个存储库接口,该接口是通过Elount repository实现的,我在UserController中使用该存储库: # app/controllers/UsersController.php use Gas\Storage\User\UserRepositoryInterface as User; class UsersController extends \BaseController {

我正在使用laravel(4.2)框架开发一个web应用程序(PHP5.4.25)。我创建了一个存储库接口,该接口是通过Elount repository实现的,我在UserController中使用该存储库:

#   app/controllers/UsersController.php

use Gas\Storage\User\UserRepositoryInterface as User;

class UsersController extends \BaseController {
    protected $user;

    public function __construct(User $user) {
        $this->user = $user;

    }

    public function store() {
        $input = Input::all();
        $validator = Validator::make(Input::all(), $this->user->getRoles());

        if ( $validator->passes() ) {
            $this->user->getUser()->username = Input::get('username');
            $this->user->getUser()->password = Hash::make(Input::get('password'));
            $this->user->getUser()->first_name = Input::get('first_name');
            $this->user->getUser()->last_name = Input::get('last_name');
            $this->user->getUser()->email = Input::get('email');
            $this->user->save();


            return false;
        } else {
            return true;
        }
    }
}
我的存储库实现:

namespace Gas\Storage\User;

#   app/lib/Gas/Storage/User/EloquentUserRepository.php

use User;

class EloquentUserRepository implements UserRepositoryInterface {

    public $_eloquentUser;

    public function __construct(User $user) {
        $this->_eloquentUser = $user;
    }

    public function all()
    {
        return User::all();
    }

    public function find($id)
    {
        return User::find($id);
    }

    public function create($input)
    {
        return User::create($input);
    }

    public function save()
    {
        $this->_eloquentUser->save();
    }

    public function getRoles()
    {
        return User::$rules;
    }

    public function getUser()
    {
        return $this->_eloquentUser;
    }
}
我还创建了一个UsersControllerTest来测试控制器,一切正常,用户被添加到数据库中。在我模拟了我的UserRepositoryInterface之后,因为我不需要测试DB insert,但我只想测试控制器

class UsersControllerTest extends TestCase {
    private $mock;

    public function setUp() {
        parent::setUp();
    }

    public function tearDown() {
        Mockery::close();
    }

    public function mock($class) {
        $mock = Mockery::mock($class);

        $this->app->instance($class, $mock);

        return $mock;
    }

    public function testStore() {
        $this->mock = $this->mock('Gas\Storage\User\UserRepositoryInterface[save]');

        $this->mock
            ->shouldReceive('save')
            ->once();

        $data['username'] = 'xxxxxx';
        $data['first_name'] = 'xxxx';
        $data['last_name'] = 'xxxx';
        $data['email'] = 'prova@gmail.com';
        $data['password'] = 'password';
        $data['password_confirmation'] = 'password';

        $response = $this->call('POST', 'users', $data);

        var_dump($response->getContent());
    }
}
我的ruote文件:

Route::resource('users', 'UsersController');
运行测试时,出现以下错误:

Mockery\Exception\InvalidCountException : Method save() from Mockery_0_Gas_Storage_User_UserRepositoryInterface should be called
 exactly 1 times but called 0 times.

为什么未调用模拟方法save

怎么了


编辑:没有部分模拟,所有都可以正常工作,现在的问题是:为什么部分模拟不起作用



谢谢

您告诉mock希望使用
save()
方法,但是
save()
在存储库中的雄辩模型上,而不是您正在模拟的存储库上

您的代码当前正在泄漏存储库实现的详细信息

而不是打电话:

$this->user->getUser()->username = Input::get('username');
您需要将
用户的实例
传递到存储库中:

$this->user->add(User::create(Input::all());
或者将
Input
数组传递到存储库中,并允许存储库在内部创建新的
User
实例:

$this->user->add(Input::all());
然后在测试中模拟
add()
方法:

$this->mock->shouldReceive('add')->once();

关于Laravel不适合模拟或单元测试的评论是错误的。

您告诉模拟希望使用
save()
方法,但是
save()
在存储库中的雄辩模型上,而不是在您模拟的存储库上

您的代码当前正在泄漏存储库实现的详细信息

而不是打电话:

$this->user->getUser()->username = Input::get('username');
您需要将
用户的实例
传递到存储库中:

$this->user->add(User::create(Input::all());
或者将
Input
数组传递到存储库中,并允许存储库在内部创建新的
User
实例:

$this->user->add(Input::all());
然后在测试中模拟
add()
方法:

$this->mock->shouldReceive('add')->once();

关于Laravel不适合模拟或单元测试的评论是错误的。

您告诉模拟希望使用
save()
方法,但是
save()
在存储库中的雄辩模型上,而不是在您模拟的存储库上

您的代码当前正在泄漏存储库实现的详细信息

而不是打电话:

$this->user->getUser()->username = Input::get('username');
您需要将
用户的实例
传递到存储库中:

$this->user->add(User::create(Input::all());
或者将
Input
数组传递到存储库中,并允许存储库在内部创建新的
User
实例:

$this->user->add(Input::all());
然后在测试中模拟
add()
方法:

$this->mock->shouldReceive('add')->once();

关于Laravel不适合模拟或单元测试的评论是错误的。

您告诉模拟希望使用
save()
方法,但是
save()
在存储库中的雄辩模型上,而不是在您模拟的存储库上

您的代码当前正在泄漏存储库实现的详细信息

而不是打电话:

$this->user->getUser()->username = Input::get('username');
您需要将
用户的实例
传递到存储库中:

$this->user->add(User::create(Input::all());
或者将
Input
数组传递到存储库中,并允许存储库在内部创建新的
User
实例:

$this->user->add(Input::all());
然后在测试中模拟
add()
方法:

$this->mock->shouldReceive('add')->once();

关于Laravel不适合模拟或单元测试的评论是错误的。

回顾您的代码,似乎您应该能够通过将模拟函数更改为以下内容来使用部分模拟:

public function mock($class) {
    $mock = Mockery::mock($class);
    $ioc_binding = preg_replace('/\[.*\]/', '', $class);
    $this->app->instance($ioc_binding, $mock);

    return $mock;
}

回顾您的代码,似乎您应该能够通过将模拟函数更改为以下内容来使用部分模拟:

public function mock($class) {
    $mock = Mockery::mock($class);
    $ioc_binding = preg_replace('/\[.*\]/', '', $class);
    $this->app->instance($ioc_binding, $mock);

    return $mock;
}

回顾您的代码,似乎您应该能够通过将模拟函数更改为以下内容来使用部分模拟:

public function mock($class) {
    $mock = Mockery::mock($class);
    $ioc_binding = preg_replace('/\[.*\]/', '', $class);
    $this->app->instance($ioc_binding, $mock);

    return $mock;
}

回顾您的代码,似乎您应该能够通过将模拟函数更改为以下内容来使用部分模拟:

public function mock($class) {
    $mock = Mockery::mock($class);
    $ioc_binding = preg_replace('/\[.*\]/', '', $class);
    $this->app->instance($ioc_binding, $mock);

    return $mock;
}


看起来要模拟的类的命名空间在“mock”方法调用中与控制器上的“use”语句中不同。一个以“Gas”开头,另一个以“Way”开头。@patricksayshi我在写问题时犯了一个错误,你在没有这个
[save]
部分的情况下试过了吗?这似乎会混淆IoC容器。也就是说,如果控制器构造函数的类型提示为“Gas\Storage\User\UserRepositoryInterface[save]”,那么它将返回正确的mock。但是它的类型暗示期望“Gas\Storage\User\UserRepositoryInterface”,因此IoC容器将返回您以前绑定到该接口的任何内容。您希望它覆盖原始的IoC绑定,但由于存在“[save]”,并且它是一个不同的字符串,我希望它只会创建一个新绑定,而不会将模拟返回测试。@hakre OP的代码中没有不能模拟的内容。我每天都在laravel中对对象进行单元测试和模拟。您是对的,您不能模拟对模型的静态调用。但OP没有这些。对facades(Input::all()、Validator::make()等)的静态调用确实可以模拟,这是Laravel设计的一部分。我仍然认为这里的问题是“[save]”字符串混淆了IoC容器。删除它应该可以解决这个问题,这时philipbrown下面的答案将变得非常重要。@PapaSmurf我想你是想回答我。它不适用于部分模拟,因为在IoC容器上,您将
UserRepositoryInterface[save]
绑定到您创建的模拟。但是应用程序代码从不要求IoC容器提供
UserRepositoryInterface[save]