How to mock an Eloquent Model using Mockery? - php

I'm trying this:
$this->dsMock = Mockery::mock('Eloquent', 'API\V1\DataSet');
$this->app->instance('API\V1\DataSet', $this->dsMock);
$this->dsMock->shouldReceive('isLocalData')->once()->andReturn(true);
Then, inside the class under test:
$test = DataSet::isLocalData($dataSetId);
However, the DataSet class is not being mocked. It's still trying to access the database. Why?

The likely problem is Laravel's unfortunate over use of Façade's (which are also factories). If DataSet was already instantiated using the Façade, it will keep returning the same class, you won't get a mocked version.
I can't remember off hand if you can instantiate your class without using the Façade in Laravel. You have to remember that when you call DataSet statically in the application, you're actually not referencing API\V1\DataSet but something else that manages it.

Related

Pass class by config using ::class and retrieve in Laravel

I have a config file with such array:
'ppr' => [
'validate' => TestRequest::class
];
Now, I want to retrive this class in other part of the system and use it to validate form (outside of the controller).
While using config('main.ppr.validate') all I receive is namespaced name of the class instead of the class object.
As I already accepted that it won't be that easy to just use reuqests as in controllers, I still do wonder how to pass a class by config.
While passing eloquent models it works like a charm (or i.e. config arrays with middlewares etc.), so I suppose there is some magic binding to the IoC to achive that, is it true?
My question is, how to use class passed as in example above without initializing it like:
$obj = new $className;
Laravel (and many other applications) use Dependency Injection to achieve this magic -- your words, not mine! :D
It seems that the Service Container is what handles this in Laravel and should be of help to you.
Directly from the Laravel docs (linked above):
Within a service provider, you always have access to the container via the $this->app property. We can register a binding using the bind method, passing the class or interface name that we wish to register along with a Closure that returns an instance of the class:
$this->app->bind('HelpSpot\API', function ($app) {
return new HelpSpot\API($app->make('HttpClient'));
});
Also:
You may use the make method to resolve a class instance out of the container. The make method accepts the name of the class or interface you wish to resolve:
$api = $this->app->make('HelpSpot\API');
And:
If some of your class' dependencies are not resolvable via the container, you may inject them by passing them as an associative array into the makeWith method:
$api = $this->app->makeWith('HelpSpot\API', ['id' => 1]);
IMHO, I would look up where/how this is implemented in the native Laravel code (usually the Illuminate vendor) and see how it is used / meant to be implemented.
Furthermore, ClassName::class will return the namespace + class of that class. This is why you only see the class name and are not actually receiving an object/instance of that class.
I'm not sure what/where/why/how you're implementing your class and why you need this functionality somewhere that it doesn't already exist. Laravel is pretty good about already having things set up where you need them, so think twice before breaking out of the box and make sure there isn't a default solution for your situation!

Mocking a method on dynamically allocated instance?

Background: I'm working on an MVC framework for some practice, and want to make sure everything is 100% unit tested.
The setup currently is to have an instance of the application class (Ex_App). The main script asks a Dispatcher/Router for a controller name. This controller name is the name of a class implementing Ex_Controller. The result is returned as an instance of Ex_Dispatch_Result. This result is passed to the Ex_App instance using an invokeController($dispatchResult) function.
And this is where magic happens. The listing below is an excerpt:
$controllerName = $dispatchResult->getControllerName();
... checks for validaty of class name ...
$controller = new $controllerName();
$controller->prepare($this);
I'm using PHPUnit to do my unit testing, and am able to mock the dispatch result, correctly check that validating the class name of the controller works. The problem is how to check if prepare is called.
I'd like to do something similar to:
$mockController = $this->getMockBuilder('Ex_Controller')
->setMockClassName('Invoke_Correct_Controller')
->getMock();
$mockController->expects($this->once())->method('prepare');
However since a new instance of Invoke_Correct_Controller is created upon calling invokeController, it will not be this mock and thus the expects() call is completely irrelevant.
I could make the Ex_Dispatch_Result class responsible for returning a controller and testing that, but before returning an instance I will need to verify the correctness of the class name and in my opinion that responsibility should be with the Ex_App class and not the "dumb shell" Ex_Dispatch_Result class.
Is there something I am missing in the PHPUnit framework that I could use to test the code here, or some useful pattern that could work in my instance? I feel passing around controller names scales way better than passing around instances of controllers from the start, requiring the initialization of every possible controller. So, I kinda want to stick to passing around names and using the Ex_App as a factory for the controller instance.
Maybe I'm just over-thinking part of this problem, but that happens sometimes. It's why a fresh look by a third party often works :-)
There are couple of things you could do:
Extract controller creation logic to separate class e.g. ControllerFactory, and then mock controller factory instance, so that it returns your $mockController.
Extract controller creation logic to separate method and use partial mocking.
Return $mockController from $dispatchResult->getControllerName(), which probably requires mocking of $dispatchResult or even something else.
If you want more detailed answer, please provide more code samples of your classes and methods.

Laravel Package Development simple function does not work

Sorry for the bad title, but I had no idea to give a good title for my problem.
I created a package with the workbench. And now I have a package. Let's say strernd/mypkg
Inside the workbench directory Strernd/Mypkg I've got following code:
http://laravel.io/bin/oNzoa
Why does Mypkg::test()work and $mypkg = new Mypkg(); $mypkg->test(); not? (Inside my app)
The error is
Call to undefined method Strernd\Mypkg\Facades\Mypkg::test()Call to undefined method Strernd\Mypkg\Facades\Mypkg::test()
I think I'm not understanding some basic principles of PHP here. I'm more like a copy&paste / try&error "developer", but it works out well in most cases.
When using it the 'static' way, Laravel is accessing the facade you wrote and instantiating the underlying class, then calling the method on that instance. When you are calling the test method with new Mypkg, you are actually instantiating the facade class, and not the underlying class. That facade class does not have a test method on it, hence the error.
Try it like this:
use Strernd\Mypkg as MypkgActual;
$mypkg = new MypkgActual;
$mypkg->test();
I believe that, because your facade and actual class implementation have the same exact class name, you're running in to a pseudo-class name conflict with Laravel's facade system winning out. You can also try changing the name of your facade to MypkgFacade.

Mock frameworks returns class with different name and type

I'm trying to create a mock to satisfy a typehint with this code (Mockery):
return \Mockery::mock('\Contracts\Helpers\iFileSystemWrapper');
or this (PHPUnit):
return $this->getMock('\Contracts\Helpers\iFileSystemWrapper');
But the mock returned is called Mockery\Mock Object or Mock_iFileSystemWrapper_a5f91049. How am I supposed to type check this when it isn't an instance of what I need at all with either framework?
Why exactly is the mock framework trying to load the real class? If I wanted the real class I would include the real class.
This problem has slowed me down so many times when writing tests I'm about to just toss type hinting out the window and check class names instead, or simply use production objects as mocks are a pain to use.
I just experimented with an existing test of my own, and by changing the interface namespace name from one that exists to one that doesn't exist, I got exactly the same as what you describe (using phpunit). My mock object had the class name Mock_ViewInterface_c755461e. When I change it back to the correct interface name, it works fine.
Therefore I would say that either:
You are trying to use an interface name that doesn't exist (e.g. a typo or missing namespace component).
Your library code isn't being loaded for some reason, e.g. autoloading is not setup correctly in your unit test bootstrap.
You need use a special function to check base class.
Somthing like this:
$mock = $this->getMock('MyClass');
$this->assertInstanceOf('MyClass', $mock);

Mock Objects in PHPUnit to emulate Static Method Calls?

I am trying to test a class that manages data access in the database (you know, CRUD, essentially). The DB library we're using happens to have an API wherein you first get the table object by a static call:
function getFoo($id) {
$MyTableRepresentation = DB_DataObject::factory("mytable");
$MyTableRepresentation->get($id);
... do some stuff
return $somedata
}
...you get the idea.
We're trying to test this method, but mocking the DataObject stuff so that (a) we don't need an actual db connection for the test, and (b) we don't even need to include the DB_DataObject lib for the test.
However, in PHPUnit I can't seem to get $this->getMock() to appropriately set up a static call. I have...
$DB_DataObject = $this->getMock('DB_DataObject', array('factory'));
...but the test still says unknown method "factory". I know it's creating the object, because before it said it couldn't find DB_DataObject. Now it can. But, no method?
What I really want to do is to have two mock objects, one for the table object returned as well. So, not only do I need to specify that factory is a static call, but also that it returns some specified other mock object that I've already set up.
I should mention as a caveat that I did this in SimpleTest a while ago (can't find the code) and it worked fine.
What gives?
[UPDATE]
I am starting to grasp that it has something to do with expects()
I agree with both of you that it would be better not to use a static call. However, I guess I forgot to mention that DB_DataObject is a third party library, and the static call is their best practice for their code usage, not ours. There are other ways to use their objects that involve constructing the returned object directly. It just leaves those darned include/require statements in whatever class file is using that DB_DO class. That sucks because the tests will break (or just not be isolated) if you're meanwhile trying to mock a class of the same name in your test--at least I think.
When you cannot alter the library, alter your access of it. Refactor all calls to DB_DataObject::factory() to an instance method in your code:
function getFoo($id) {
$MyTableRepresentation = $this->getTable("mytable");
$MyTableRepresentation->get($id);
... do some stuff
return $somedata
}
function getTable($table) {
return DB_DataObject::factory($table);
}
Now you can use a partial mock of the class you're testing and have getTable() return a mock table object.
function testMyTable() {
$dao = $this->getMock('MyTableDao', array('getMock'));
$table = $this->getMock('DB_DataObject', ...);
$dao->expects($this->any())
->method('getTable')
->with('mytable')
->will($this->returnValue($table));
$table->expects...
...test...
}
This is a good example of a dependency in your code - the design has made it impossible to inject in a Mock rather than the real class.
My first suggestion would be to try and refactor the code to use an instance rather than a static call.
What's missing (or not?) from your DB_DataObject class is a setter to pass a prepared db object before calling the factory method. That way you can pass a mock or a custom db object (with the same interface) should the need arise.
In your test setup:
public function setUp() {
$mockDb = new MockDb();
DB_DataObject::setAdapter($mockDb);
}
The factory() method should return the mocked DB instance. If it's not already integrated into your class, you will probably have to refactor the factory() method as well to make it work.
Are you require/including the class file for DB_DataObject in your test case? If the class doesn't exist before PHPUnit tries to mock the object you can get errors like this.
With PHPUnit MockFunction extension plus runkit you can also mock static methods. Be careful, because it's monkey patching and therefore should only be used in extreme cases. Does not substitute good programming practices.
https://github.com/tcz/phpunit-mockfunction

Categories