4
votes

How to use mock in lumen framework? I use the Lumen framework. The documents of lumen are very simple. I don't know how to use mockery or facades to mock Models. I tried some means, but no one worked. I want to mock two points of UserModel in updatePassword method. Please help me.

UserModel

use Illuminate\Database\Eloquent\Model;
class UserModel extends Model {
    // connection
    protected $connection = 'db_user';
    // table
    protected $table = 'user';
    // primarykey
    protected $primaryKey = 'id';
}

UserLogic

class UserLogic {
    public static updatePassword($id, $password) {
        // find user
        $user = UserModel::find($id); // mock here**************************************
        if (empty($user)) {
            return 'not find user';
        }
        // update password
        $res = UserModel::where('id', $id)
               ->update(['password' => $password]); // mock here*****************************
        if (false == $res) {
            return 'update failed';
        }
        // re Login
        $res2 = LoginModel::clearSession();
        if (false == $res2) {
            return false;
        }
        return true;
    }
}

phpunit test 1 don't work

use Mockery;
public function testUpdatePassword() {
    $mock = Mockery:mock('db_user');
    $mock->shouldReceive('find')->andReturn(false);
    $res = UserLogic::updatePassword(1, '123456');
    $this->assertEquals('not find user', $res);
}

phpunit test 2

// BadMethodCallException: Method Mockery_0_Illuminate_DatabaseManager::connection() does not exist on this mock object
use Illuminate\Support\Facades\DB;
public function testUpdatePassword() {
    DB::shouldReceive('select')->andReturnNull();
    $res = UserLogic::updatePassword(1, '123456');
    $this->assertEquals('not find user', $res);
}

phpunit test 3 don't work

use Mockery;
public function testUpdatePassword() {
    $mock = Mockery::mock('alias:UserModel');
    $mock->shouldReceive('find')->andReturn(null);
    $res = UserLogic::updatePassword(1, '123456');
    $this->assertEquals('not find user', $res);
}
1
Same stuff here. I've tried PHPUnit, Mockery, AspectMock and still can't get mocking to work for this framework and ORM models.Peter Badida
Using plain PHPUnit TestCase instead of Lumen TestCase worked for me, see this github issuePeter Badida

1 Answers

1
votes

Try this:

    $mock = Mockery::mock('alias:\NamespacePath\To\UserModel');
    $mock->shouldReceive('find')->andReturn(null);
    $this->app->instance('\NamespacePath\To\UserModel', $mock);

Note that when you want to mock public static functions, you use the keyword alias. If you you want to mock an object instead (created with new), you use the keyword overload instead (What is the difference between overload and alias in Mockery?).

Another difficulty I faced with the mocks was that they we're still used in other tests in which I didn't want to use the mocks anymore and this made the tests fail. The mock persistency has to do with the loading of classes. This happens once on global scope and stays until the test process terminates (from here: https://blog.gougousis.net/mocking-static-methods-with-mockery/). So whatever comes first (using the class as mock or as the real thing) will define how the class is called and you can't change it anymore after that. In the blog post I linked a few lines earlier, Alexandros Gougousis talks about how to circumvent this problem by running the mock tests in their own processes and disabling global state preservation. I tried out his approach but had some issues but I also didn't spend that much time. Why not? Because I found a different solution: In the phpunit.xml set the variable processIsolation="true". This solved the problem. I should note that this increased the duration of each test by approx. 0.5s so Alexandros approach might be more efficient if you get it to run as it will only run the tests with mocks in their own processes.

Side note: This also worked with the Lumen TestCase as the base class of the tests.