I'm currently in the process of writing unit tests for a Trait. This Trait included a method called registryPersist() which in tern calls a Facade method called Registry::persist(). This method as the name implies saves the registry value to the database and then calls Registry::set() in order to set the registry to the local scope. It is a fairly straight forward procedure.
I tried following the suggestion from the Laravel docs and mocking the Registry::persist() method with the following code:
Registry::shouldReceive('persist')->with('test', 'ok', 'model.users')
However given that persist also calls set i get the BadMethodCallException exception because set is not mocked. (Received RegistryAccessControl::set(), but no expectations were specified)
The official mockery docs suggest to create a mock instance how ever this will not work because when i actually try to call the Trait method, the facade is not mocked.
$user = factory(User::class)->create();
$user->registrySet('test', 'ok');
The Trait Method:
public function registryPersist(string $name, $value): void
{
Registry::persist(
$name,
$value,
$this->getNamespace()
);
}
Is there any way to effectively test this method?
Furthermore, given that registrySet() only forwards the parameters to the Facade do you think that the test that im trying to write is a good idea?
Thanks in advance.
Related
tell me how you can make fun of the interface. Otherwise, you replace the interface with the implemented class, then the test passes. The most interesting thing is that the code works with the interface.
Gist
With the interface, I get the error:
Mockery\Exception\InvalidCountException : Method send(<Any Arguments>) from Mockery_0_App_Services_SmsNotification_SmsInterface should be called
exactly 1 times but called 0 times
How do I implement the test?
On line 36 of ExampleTest.php,
$user->notify(new CreateUploadUpdateTask(new SmsMessage()));
The notify() method comes from Illuminate\Notifications\Notifiable trait, right? If so, you can fake the notification as described in this link.
Besides, if you send notifications using the Notification facade, then you can mock the facade as described in this link.
Please try to name your test classes and methods as descriptive as possible so that one can quickly realize what are you trying to test just from reading the test class or method name.
I have been trying to set up a unit test that calls a method where the database is called. I have looked up mocking and faker and am confused about what to do.
In my test I call
Logic::lookForClassificationException($exception_array, $content);
In my method there is a call to my ClassificationException model
$exceptions = ClassificationException::select('exception')->get();
I am confused about how to set up a mock for this. ClassificationException is a table that has an id, exception, previous_word, and after_word.
How do I mock or fake the setup?
One way to do tests using DB is to setup an in memory sqlite database for tests like so:
use Illuminate\Database\Capsule\Manager as DB;
protected function setUpDatabase()
{
$database = new DB;
$database->addConnection(['driver' => 'sqlite', 'database' => ':memory:']);
$database->bootEloquent();
$database->setAsGlobal();
}
Then, call this function in your setUp method of the test class.
This is just one of many possible ways and may not suit your needs. Another way would be to setup a separate database for tests and use a separate .env for tests
In my experience, tests break down when you try to mock Eloquent models
I have a codeception cest test in which I want to check if some method is called.
public function my_test(FunctionalTester $I)
{
$I->authenticateTestUser($I);
$this->fillKomitentForm($I, 'kom1', 'Komitent 1');
$I->click('btnSave');
// Here I want to check that MyService.myMethod() is invoked
}
I can make a helper but anyway I do not know how to assure that some method on some object is called. Take into account that this is Laravel 5.2 and that those services are bind using service providers.
Try to use AspectMock.
One of the features is Create test doubles for class methods called anywhere.
How can I call a controller method manually specifying some input parameters yet still have method injection work for the parameters not specified (example below).
routes.php
$myController->index($id);
controllers/MyControllerOne.php
class MyControllerOne
{
public function index($id, MyRequest $request)
{
}
}
extra information
The reason I need this is because I have special information in my routes that determines which controller should be executed such as /myroute/{data}/{id}. It's a bit unorthodox but it's a necessary evil given the scope of our system.
Once I resolve within my routes which controller needs to be called I then want to call the method on that controller. $controllerInstance->index($id).
If it's only for Request, I think you could manually pass this $this->app->make('Request'), like so
$controllerIntance->index($id, $this->app->make('Request'))
Note that you actually don't have to inject Request, since you might as well use App::make inside of your controller. But I'm not sure how good this decision is in case of testability and coupling.
For more info:
This function resolves 'Request' out of the container, that is instantiates or returns an existing instance (depending of the type of service provider).
Using make is described here http://laravel.com/docs/5.0/container (see "Resolving"). I also found this answer helpful, to understanding how the container works https://stackoverflow.com/a/25798288/1627227
I am not sure if I am going about this the right way but here is what I am attempting to do, if there is a better way please let me know.
I am using a service provider that pulls some data from a config file. the problem is that if I use Config::set to change one of the settings after calling a function that uses that service provider it will not update. I thought that because I am using app->bind instead of app->share that it would re instantiate the class every time. here is my code:
service provider:
public function Register() {
$app = $this->app;
$app->bind('\path\to\MyInterface', function() use($app) {
$server = $app['config']->get('myconfig.server');
$client = $app['config']->get('myconfig.client');
$key = $app['config']->get('myconfig.key');
$version = $app['config']->get('myconfig.version');
return new MyService(new Instance($server, $client, $key, $version));
});
$this->app->booting(function() {
$loader = \Illuminate\Foundation\AliasLoader::getInstance();
$loader->alias('MyServiceFacade', '\path\to\MyFacade');
});
}
Facade class:
class MyServiceFacade extends Facade {
protected static function getFacadeAccessor() { return '\path\to\MyInterface'; }
}
route for testing:
Route::get('test', function() {
$nodes = MyServiceFacade::allNodes();
\Config::set('myconfig.server', 'new server name');
$nodes2 = MyServiceFacade::allNodes();
var_dump($nodes->getContent());
var_dump($nodes2->getContent());
}
);
I am getting the same results from both. shouldn't this be using the update config since I am making a new instance of the controller?
Skip to update 2 below for a stab at the answer
Your question doesn't quite make sense. In your testing route you're saying
App::make('MyController');
This is you asking Laravel to make an instance of the MyController service and/or class. However, you never define a MyController service and/or class.
You bind a \pathto\Interface identifier here
$app->bind('\pathto\Interface', ...
and alias MyService to that identifier here
$loader->alias('MyService', '\pathto\Facade');
but there's no place you bind or alias a MyController identifier anywhere. There's nothing in your code samples that tie MyController to the service you have bound.
Because of that it's not 100% clear what you're asking.
Update: Your question still doesn't quite make sense, and I think this not-sense-masking is what's leading the the unexpected behavior. i.e., you're doing something that "works", as in PHP doesn't complain with an error, but what you think is happening behind the scenes is not happening.
You've refereed to MyService as a facade -- however, you haven't told us what the "facade accessor" string the MyService facade points to (via its getFacadeAccessor method). Also, you appear to be directly instantiating a class from that facade class (new MyService), which isn't how Laravel facades work.
Update 2: The code samples provided are still a little sketchy, and I suspect they don't accurately reflect the actual application. The context from the comments are that MyServiceFacade::allNodes is a call to a facade. However, the facade defined in the code samples is named MyFacade and there's no class MyServiceFacade. I'm going to take a stab based on something mentioned in a comment as to the problem, but based on what I've seen above the problem still might be an incorrect application of service providers, services, and facades.
Binding a service with bind ensures the application container will always return a new instance of the service. I bet if you tried something like the following
$app = app();
$object = $app['\path\to\MyInterface'];
You'd find your object is instantiated anew every time. Adding some basic var_dump debugging to the bound closure and/or service class constructor is a good way to confirm that.
However, Laravel facades are a little different. They're not, technically, a part of the application container system. Facades are a second system built on top of the application container.
In addition to providing a convenient alias for accessing a service class, the facade implementation also forces the service object into a single-instance/singleton irrespective of how you've bound it into the container. This happens in the base facade class here
#File: vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php
protected static function resolveFacadeInstance($name)
{
if (is_object($name)) return $name;
if (isset(static::$resolvedInstance[$name]))
{
return static::$resolvedInstance[$name];
}
return static::$resolvedInstance[$name] = static::$app[$name];
}
The base facade class keeps an instance cache in static::$resolvedInstance. The specifics of how Laravel gets here are a bit long for a StackExchange answer, but my Unraveling Laravel Facades article (part of a longer series) is a good place to start.
The problem here (again, based on the incomplete information provided) appears to be a misunderstanding of facades. The main takeaway is a facade always forces a service object to be a singleton.