laravel5 + REST + mocks not working #2904

Closed
rmorgan105 opened this Issue Mar 14, 2016 · 17 comments

Comments

Projects
None yet
5 participants
@rmorgan105

Does anyone know how to use mocks with the REST module and Laravel5 module as the beackend?
It seems that the application is being refreshed every time I do a sendXXX call, which wipes out any mocks I've setup.

@janhenkgerritsen

This comment has been minimized.

Show comment
Hide comment
@janhenkgerritsen

janhenkgerritsen Mar 15, 2016

Contributor

The Laravel 5 module indeed refreshes the application between each request. But what are you trying to do that you need mocks in functional tests between requests? This probably is not a very good approach, whatever you want to achieve.

Contributor

janhenkgerritsen commented Mar 15, 2016

The Laravel 5 module indeed refreshes the application between each request. But what are you trying to do that you need mocks in functional tests between requests? This probably is not a very good approach, whatever you want to achieve.

@rmorgan105

This comment has been minimized.

Show comment
Hide comment
@rmorgan105

rmorgan105 Mar 15, 2016

I'm working on a application that talks to several external services. I want to mock the responses of these services rather than hit them from the automated tests.
Isn't it the whole point of dependancy injection to be able to provide alternative implementations of services without having to mix test and production code?

I'm working on a application that talks to several external services. I want to mock the responses of these services rather than hit them from the automated tests.
Isn't it the whole point of dependancy injection to be able to provide alternative implementations of services without having to mix test and production code?

@janhenkgerritsen

This comment has been minimized.

Show comment
Hide comment
@janhenkgerritsen

janhenkgerritsen Mar 15, 2016

Contributor

Can't you just separate your requests in a test for each request?

Another option is to set your mocks in a service provider. You can just check if you are running in the test environment and if so add your mocks to the dependency injection container.

Contributor

janhenkgerritsen commented Mar 15, 2016

Can't you just separate your requests in a test for each request?

Another option is to set your mocks in a service provider. You can just check if you are running in the test environment and if so add your mocks to the dependency injection container.

@rmorgan105

This comment has been minimized.

Show comment
Hide comment
@rmorgan105

rmorgan105 Mar 15, 2016

I did end up configuring the service provider to create the mocks, but it's a bit of a code smell. I'm configuring test requirements out side of the tests, and it doesn't provide much flexibility in how the mocks respond.
I understand why the app refreshes on each call. It's just a shame there's no way to configure the app from the tests before the call.

I did end up configuring the service provider to create the mocks, but it's a bit of a code smell. I'm configuring test requirements out side of the tests, and it doesn't provide much flexibility in how the mocks respond.
I understand why the app refreshes on each call. It's just a shame there's no way to configure the app from the tests before the call.

@janhenkgerritsen

This comment has been minimized.

Show comment
Hide comment
@janhenkgerritsen

janhenkgerritsen Mar 15, 2016

Contributor

I am open to suggestions, what do you think would be a nice way of doing this? Some sample code of how you would like to use such a feature would be great.

Contributor

janhenkgerritsen commented Mar 15, 2016

I am open to suggestions, what do you think would be a nice way of doing this? Some sample code of how you would like to use such a feature would be great.

@torkiljohnsen

This comment has been minimized.

Show comment
Hide comment
@torkiljohnsen

torkiljohnsen May 27, 2016

Contributor

@janhenkgerritsen I'm having the same issue. Want to mock a repository that collects data from an external API to avoid hitting the external API in tests.

I tried to hack something together in my functional suite:

<?php 

use App\Integrations\Vendor\Repo;

$I = new FunctionalTester($scenario);
$I->wantTo('Import products from external API into my app');

$repo = Mockery::mock(Repo::class);
$repo->shouldReceive('all')->andReturn(['foo' => 'bar']);

$I->bindService(Repo::class, $repo); // or bindInstance
$I->amOnRoute('import'); // in here, repo->all should be called, but the mock is not called, the actual repo is used instead
// ... test that I see foo => bar

In my FunctionalHelper.php I tried to add this:

public function bindService($abstract, $instance, $shared = false)
{
    $laravel = $this->getModule('Laravel5');
    $app = $laravel->getApplication();
    $app->bind($abstract, $instance, $shared = false);
    $laravel->setApplication($app);
}

// Also tried similar bindInstance:
public function bindInstance($abstract, $instance)
{
    $laravel = $this->getModule('Laravel5');
    $app = $laravel->getApplication();
    $app->instance($abstract, $instance);
    $laravel->setApplication($app);
}

Problem is that the mock is never used, only the actual Repo class.

Here is a solution that overrides the connection class to achieve this:
http://stagerightlabs.com/blog/using-mockery-with-codeception-and-laravel-4

I have googled a bit and others are asking about the same, for instance here:
https://laracasts.com/discuss/channels/testing/ioc-container-mocks-and-codeception-integration-testing

Contributor

torkiljohnsen commented May 27, 2016

@janhenkgerritsen I'm having the same issue. Want to mock a repository that collects data from an external API to avoid hitting the external API in tests.

I tried to hack something together in my functional suite:

<?php 

use App\Integrations\Vendor\Repo;

$I = new FunctionalTester($scenario);
$I->wantTo('Import products from external API into my app');

$repo = Mockery::mock(Repo::class);
$repo->shouldReceive('all')->andReturn(['foo' => 'bar']);

$I->bindService(Repo::class, $repo); // or bindInstance
$I->amOnRoute('import'); // in here, repo->all should be called, but the mock is not called, the actual repo is used instead
// ... test that I see foo => bar

In my FunctionalHelper.php I tried to add this:

public function bindService($abstract, $instance, $shared = false)
{
    $laravel = $this->getModule('Laravel5');
    $app = $laravel->getApplication();
    $app->bind($abstract, $instance, $shared = false);
    $laravel->setApplication($app);
}

// Also tried similar bindInstance:
public function bindInstance($abstract, $instance)
{
    $laravel = $this->getModule('Laravel5');
    $app = $laravel->getApplication();
    $app->instance($abstract, $instance);
    $laravel->setApplication($app);
}

Problem is that the mock is never used, only the actual Repo class.

Here is a solution that overrides the connection class to achieve this:
http://stagerightlabs.com/blog/using-mockery-with-codeception-and-laravel-4

I have googled a bit and others are asking about the same, for instance here:
https://laracasts.com/discuss/channels/testing/ioc-container-mocks-and-codeception-integration-testing

@janhenkgerritsen

This comment has been minimized.

Show comment
Hide comment
@janhenkgerritsen

janhenkgerritsen May 27, 2016

Contributor

I will look into this issue after I come back from my holiday.

Contributor

janhenkgerritsen commented May 27, 2016

I will look into this issue after I come back from my holiday.

@DavertMik

This comment has been minimized.

Show comment
Hide comment
@DavertMik

DavertMik Jun 17, 2016

Member

I think we can use approach similar to @torkiljohnsen uses to register his mocks and apply them after each request.

We can have

  • haveMockedService() method
  • haveMockedInstance
  • ... etc

before a request is made all mocked things are injected inro application

And probably a method to disable mock:

  • disableMock($serviceOrInstanceName)
Member

DavertMik commented Jun 17, 2016

I think we can use approach similar to @torkiljohnsen uses to register his mocks and apply them after each request.

We can have

  • haveMockedService() method
  • haveMockedInstance
  • ... etc

before a request is made all mocked things are injected inro application

And probably a method to disable mock:

  • disableMock($serviceOrInstanceName)
@janhenkgerritsen

This comment has been minimized.

Show comment
Hide comment
@janhenkgerritsen

janhenkgerritsen Jun 17, 2016

Contributor

The Laravel IoC container has the following methods to register bindings:

// Register a binding
public function bind($abstract, $concrete = null, $shared = false);

// Register a binding if it hasn't already been registered
public function bindIf($abstract, $concrete = null, $shared = false);

// Alias for bind($abstract, $concrete, true)
public function singleton($abstract, $concrete = null);

// Register an instance
public function instance($abstract, $instance);

// When $concrete needs $abstract give $implementation
public function addContextualBinding($concrete, $abstract, $implementation);

I think we can skip bindIf for tests, but we should have methods for the other types of bindings. I think we should add the following methods:

public function haveMockedService($abstract, $concrete);
public function haveMockedSingleton($abstract, $concrete);
public function haveMockedInstance($abstract, $instance);
public function haveMockedContextualBinding($concrete, $abstract, $implementation);

As for a disableMock method, I don't think such a method is really necessary. If you want to mock some service in a test, you probably want it mocked for the whole test.

What do you guys think of this?

Contributor

janhenkgerritsen commented Jun 17, 2016

The Laravel IoC container has the following methods to register bindings:

// Register a binding
public function bind($abstract, $concrete = null, $shared = false);

// Register a binding if it hasn't already been registered
public function bindIf($abstract, $concrete = null, $shared = false);

// Alias for bind($abstract, $concrete, true)
public function singleton($abstract, $concrete = null);

// Register an instance
public function instance($abstract, $instance);

// When $concrete needs $abstract give $implementation
public function addContextualBinding($concrete, $abstract, $implementation);

I think we can skip bindIf for tests, but we should have methods for the other types of bindings. I think we should add the following methods:

public function haveMockedService($abstract, $concrete);
public function haveMockedSingleton($abstract, $concrete);
public function haveMockedInstance($abstract, $instance);
public function haveMockedContextualBinding($concrete, $abstract, $implementation);

As for a disableMock method, I don't think such a method is really necessary. If you want to mock some service in a test, you probably want it mocked for the whole test.

What do you guys think of this?

@torkiljohnsen

This comment has been minimized.

Show comment
Hide comment
@torkiljohnsen

torkiljohnsen Jun 17, 2016

Contributor

You could name it something general instead of "service", since the
container can hold all kinds of bindings.

Also think you should avoid calling it "mock", since we could be injecting
all kinds of things as test doubles, like a dummy, mock, stub, fake, spy,
etc.

Why not just reuse the Laravel naming to make things very obvious?

$I->haveBinding/haveInstance/haveSingleton/haveContextualBinding ?

fre. 17. jun. 2016 kl. 17.40 skrev Jan-Henk Gerritsen <
notifications@github.com>:

The Laravel IoC container has the following methods to register bindings:

// Register a binding
public function bind($abstract, $concrete = null, $shared = false);

// Register a binding if it hasn't already been registered
public function bindIf($abstract, $concrete = null, $shared = false);

// Alias for bind($abstract, $concrete, true)
public function singleton($abstract, $concrete = null);

// Register an instance
public function instance($abstract, $instance);

// When $concrete needs $abstract give $implementation
public function addContextualBinding($concrete, $abstract, $implementation);

I think we can skip bindIf for tests, but we should have methods for the
other types of bindings. I think we should add the following methods:

function haveMockedService($abstract, $concrete);
function haveMockedSingleton($abstract, $concrete);
function haveMockedInstance($abstract, $instance);
function haveMockedContextualBinding($concrete, $abstract, $implementation);

As for a disableMock method, I don't think such a method is really
necessary. If you want to mock some service in a test, you probably want it
mocked for the whole test.

What do you guys think of this?


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#2904 (comment),
or mute the thread
https://github.com/notifications/unsubscribe/AATaiS0gry2ga_jtqnOwoYn2SjY72ztjks5qMr_xgaJpZM4HwfLa
.

Contributor

torkiljohnsen commented Jun 17, 2016

You could name it something general instead of "service", since the
container can hold all kinds of bindings.

Also think you should avoid calling it "mock", since we could be injecting
all kinds of things as test doubles, like a dummy, mock, stub, fake, spy,
etc.

Why not just reuse the Laravel naming to make things very obvious?

$I->haveBinding/haveInstance/haveSingleton/haveContextualBinding ?

fre. 17. jun. 2016 kl. 17.40 skrev Jan-Henk Gerritsen <
notifications@github.com>:

The Laravel IoC container has the following methods to register bindings:

// Register a binding
public function bind($abstract, $concrete = null, $shared = false);

// Register a binding if it hasn't already been registered
public function bindIf($abstract, $concrete = null, $shared = false);

// Alias for bind($abstract, $concrete, true)
public function singleton($abstract, $concrete = null);

// Register an instance
public function instance($abstract, $instance);

// When $concrete needs $abstract give $implementation
public function addContextualBinding($concrete, $abstract, $implementation);

I think we can skip bindIf for tests, but we should have methods for the
other types of bindings. I think we should add the following methods:

function haveMockedService($abstract, $concrete);
function haveMockedSingleton($abstract, $concrete);
function haveMockedInstance($abstract, $instance);
function haveMockedContextualBinding($concrete, $abstract, $implementation);

As for a disableMock method, I don't think such a method is really
necessary. If you want to mock some service in a test, you probably want it
mocked for the whole test.

What do you guys think of this?


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#2904 (comment),
or mute the thread
https://github.com/notifications/unsubscribe/AATaiS0gry2ga_jtqnOwoYn2SjY72ztjks5qMr_xgaJpZM4HwfLa
.

@janhenkgerritsen

This comment has been minimized.

Show comment
Hide comment
@janhenkgerritsen

janhenkgerritsen Jun 17, 2016

Contributor

I agree with @torkiljohnsen about the naming. What do you think @DavertMik?

Contributor

janhenkgerritsen commented Jun 17, 2016

I agree with @torkiljohnsen about the naming. What do you think @DavertMik?

@DavertMik

This comment has been minimized.

Show comment
Hide comment
@DavertMik

DavertMik Jun 18, 2016

Member

I like it to 👍

Member

DavertMik commented Jun 18, 2016

I like it to 👍

@janhenkgerritsen

This comment has been minimized.

Show comment
Hide comment
@janhenkgerritsen

janhenkgerritsen Jun 20, 2016

Contributor

I implemented the functionality and merged it into the 2.2 branch. I also added tests to the sample application with commit janhenkgerritsen/codeception-laravel5-sample@3e4b49f.

If you want to use this new functionality right away and not wait for Codeception 2.2.2 you can use 2.2.x-dev as your composer version constraint for Codeception.

Contributor

janhenkgerritsen commented Jun 20, 2016

I implemented the functionality and merged it into the 2.2 branch. I also added tests to the sample application with commit janhenkgerritsen/codeception-laravel5-sample@3e4b49f.

If you want to use this new functionality right away and not wait for Codeception 2.2.2 you can use 2.2.x-dev as your composer version constraint for Codeception.

@andrevez

This comment has been minimized.

Show comment
Hide comment
@andrevez

andrevez Nov 28, 2016

Hi everyone.
Sorry, maybe I've missed something. But I have only one question, and couldn't find any answers.
Does it work with ApiTester?

Hi everyone.
Sorry, maybe I've missed something. But I have only one question, and couldn't find any answers.
Does it work with ApiTester?

@janhenkgerritsen

This comment has been minimized.

Show comment
Hide comment
@janhenkgerritsen

janhenkgerritsen Nov 28, 2016

Contributor

As long as your API tests are functional tests it should work.

Contributor

janhenkgerritsen commented Nov 28, 2016

As long as your API tests are functional tests it should work.

@andrevez

This comment has been minimized.

Show comment
Hide comment
@andrevez

andrevez Nov 28, 2016

I don't understand. What do you mean? I have api tests and functional tests. I understand that I can mock service in functional tests. But what if I need mock service in api tests.
Or you mean that I should testing API in functional tests?

I don't understand. What do you mean? I have api tests and functional tests. I understand that I can mock service in functional tests. But what if I need mock service in api tests.
Or you mean that I should testing API in functional tests?

@janhenkgerritsen

This comment has been minimized.

Show comment
Hide comment
@janhenkgerritsen

janhenkgerritsen Nov 28, 2016

Contributor

You can test your API with functional tests, for that you can use the Laravel5 module. But you could also test your API with acceptance tests, for which you can use the WebDriver module. In that case you should not use the Laravel5 module and you will not be able to use the functionality described in this issue.

For more details on the difference between functional tests and acceptance tests check out the documentation:

Contributor

janhenkgerritsen commented Nov 28, 2016

You can test your API with functional tests, for that you can use the Laravel5 module. But you could also test your API with acceptance tests, for which you can use the WebDriver module. In that case you should not use the Laravel5 module and you will not be able to use the functionality described in this issue.

For more details on the difference between functional tests and acceptance tests check out the documentation:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment