Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

laravel5 + REST + mocks not working #2904

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

laravel5 + REST + mocks not working #2904

rmorgan105 opened this issue Mar 14, 2016 · 17 comments
Assignees

Comments

@rmorgan105
Copy link

@rmorgan105 rmorgan105 commented Mar 14, 2016

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
Copy link
Contributor

@janhenkgerritsen 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
Copy link
Author

@rmorgan105 rmorgan105 commented 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?

@janhenkgerritsen
Copy link
Contributor

@janhenkgerritsen 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
Copy link
Author

@rmorgan105 rmorgan105 commented 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.

@janhenkgerritsen
Copy link
Contributor

@janhenkgerritsen 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
Copy link
Contributor

@torkiljohnsen 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 janhenkgerritsen self-assigned this May 27, 2016
@janhenkgerritsen
Copy link
Contributor

@janhenkgerritsen janhenkgerritsen commented May 27, 2016

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

@DavertMik
Copy link
Member

@DavertMik 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
Copy link
Contributor

@janhenkgerritsen 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
Copy link
Contributor

@torkiljohnsen 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
Copy link
Contributor

@janhenkgerritsen janhenkgerritsen commented Jun 17, 2016

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

@DavertMik
Copy link
Member

@DavertMik DavertMik commented Jun 18, 2016

I like it to 👍

@janhenkgerritsen
Copy link
Contributor

@janhenkgerritsen 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
Copy link

@andrevez andrevez commented 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?

@janhenkgerritsen
Copy link
Contributor

@janhenkgerritsen janhenkgerritsen commented Nov 28, 2016

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

@andrevez
Copy link

@andrevez andrevez commented 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?

@janhenkgerritsen
Copy link
Contributor

@janhenkgerritsen 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
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
5 participants
You can’t perform that action at this time.