I'm working on a Laravel (5.4) package, and I'm trying to do unit testing. I have this class :
<?php
namespace Sample;
class Foo
{
public function getConfig()
{
$config = \Config::get('test');
return $config;
}
}
I have this test :
<?php
use PHPUnit\Framework\TestCase;
use Sample\Foo;
class FooTest extends TestCase
{
public function testGetConfig()
{
$foo = new Foo;
$config = $foo->getConfig();
}
}
When I execute phpunit I have this error :
Error: Class 'Config' not found
How can I unit test this class ?
Thank you.
Instead of extends PHPUnit\Framework\TestCase, you should extends Tests\TestCase:
<?php
namespace Tests\Unit;
// use PHPUnit\Framework\TestCase;
use Tests\TestCase;
use Sample\Foo;
class FooTest extends TestCase
{
public function testGetConfig()
{
$foo = new Foo;
$config = $foo->getConfig();
}
}
Moreover, Config or other Laravel facades may not work in #dataProvider method, please see Laravel framework classes not available in PHPUnit data provider for more info.
It is good practice to mock the dependencies in your code. In this case you are depending on an outside class (Config). Usually I test it like this:
// make sure the mock config facade receives the request and returns something
Config::shouldReceive('get')->with('test')->once()->andReturn('bla');
// check if the value is returned by your getConfig().
$this->assertEquals('bla', $config);
Obviously, you need to import the Config facade in your test.
BUT: I would inject the Config class in the constructor in my real code, instead of using the facade. But that's me... :-)
Something like this
class Foo
{
/** container for injection */
private $config;
public function __construct(Config config) {
$this->config = $config;
}
public function getConfig()
{
$config = $this->config->get('test');
return $config;
}
}
And then test it by injecting a mock Config into the constructor.
Try by including like this
use Illuminate\Support\Facades\Config;
Related
I have made a simple class named MyService
namespace App\Services;
class MyService
{
private $anotherService;
public function setService(AnotherService $anotherService)
{
$this->anotherService = $anotherService;
}
public function getService()
{
if(empty($this->anotherService)){
$this->setService(new AnotherService());
}
return $this->anotherService;
}
public function call()
{
$anotherService = $this->getService();
$anotherService->SetXY(5,10);
}
}
As you can se via a setter I set as Depedency the AnotherService:
namespace App\Services;
class AnotherService
{
public function SetXY($x,$y)
{
}
}
In order to test whether the MyService runs as expected I made the following test:
namespace Tests\Services;
namespace Tests\Services;
use App\Services\MyService;
use App\Services\AnotherService;
use PHPUnit\Framework\TestCase;
use Mockery;
class MyServiceTest extends TestCase
{
public function testService()
{
$mockedAnotherService = Mockery::spy(AnotherService::class);
$mockedAnotherService->shouldReceive('SetXY');
$service = new MyService();
$service->setService($mockedAnotherService);
$service->call();
$mockedAnotherService->shouldHaveReceived()->setXY(5,10);
}
}
But for some reason seems that I am unable to assert that setXY is called despite the opposite. The error is:
1) Tests\Services\MyServiceTest::testService
Mockery\Exception\InvalidCountException: Method setXY(<Any Arguments>) from Mockery_0_App_Services_AnotherService should be called
at least 1 times but called 0 times.
/var/www/html/api/vendor/mockery/mockery/library/Mockery/CountValidator/AtLeast.php:47
/var/www/html/api/vendor/mockery/mockery/library/Mockery/Expectation.php:310
/var/www/html/api/vendor/mockery/mockery/library/Mockery/ReceivedMethodCalls.php:46
/var/www/html/api/vendor/mockery/mockery/library/Mockery/VerificationDirector.php:36
/var/www/html/api/vendor/mockery/mockery/library/Mockery/HigherOrderMessage.php:46
/var/www/html/api/tests/Services/MyServiceTest.php:23
phpvfscomposer:///var/www/html/api/vendor/phpunit/phpunit/phpunit:60
Do you know why that does happen?
There is a typo in:
$mockedAnotherService->shouldHaveReceived()->setXY(5,10);
Should be:
$mockedAnotherService->shouldHaveReceived()->SetXY(5,10);
i dont know how to write mock test for my service
here is my compressContract Interface
interface compressContract
{
public function compress();
}
here is my ZipCompress class
<?php
namespace App\Http\Controllers\My\compression;
class ZipCompress implements compressContract
{
public function compress()
{
var_dump('im zip compression');
}
}
and here is my compressManager class
class compressManager
{
public compressContract $compressContract;
public function __construct(compressContract $compressContract)
{
$this->compressContract = $compressContract;
}
public function compressFile(Request $request)
{
//do somthing
}
}
and here is AppServiceProvider class in the boot method
public function boot()
{
$this->app->bind(CompressContract::class , ZipCompress::class);
}
i read articles about mocking a class but i cant write mock for these class
Firstly basics, class names are pascal cased. compressContract should be CompressContract.
A unit test, mocking the compression contract would look like this. Laravel has helpers in the test cases to help you actual bind the mocked instance to the container. This is described in the documentation here.
/** #test **/
public function your_test()
{
$this->mock(CompressContract::class, function (MockInterface $mock) {
$mock->shouldReceive('compress')->once()->andReturn('If you want correct return');
});
// call your class or http test your controller.
}
I am mocking a class to test its methods however i can't set the required return value.
Here is what i have done
<?php
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
use Lopo\Payment;
class PaymentTest extends TestCase
{
public $stub;
public function setUp()
{
$this->stub = $this->createMock(Payment::class);
}
public function testProceedPaymentMethod()
{
$this->stub->expects($this->any())->method('proceed')
->will($this->returnValue('fooo'));
var_dump($this->stub->proceed(10)); // not returning fooo
}
}
I tried without setup method
I tried with createMock, getMockBuilder() but always getting NULL instead of the specefied return value.
how can i solve this ?
You want $this->stub->method('proceed')->willReturn('foo');.
How do initiate model from model in Laravel 4. I try the following but without any success
namespace App\Models;
use \Httpful\Request;
use \RecursiveIteratorIterator;
use \RecursiveArrayIterator;
use Illuminate\Support\Facades\DB as DB;
class Parser extends \Eloquent {
public function __construct(AffiApi $affiapi) {
$this->affiapi = $affiapi;
$url = "HTTP://some.com/xml/feed_config.xml";
$xml = Request::get($url)->expectsXml()->send();
$this->xml = $xml->body;
}
public function Test(){
$api = new Api();
//I get error Class 'App\Models\Api' not found
}
}
class Api extends Parser {
}
How di i do it properly or what is the cleanest way to achive this
The neatest and best practiced way to do this is by using a principle called dependency injection (DI). This way it is loosely coupled and it will also be possible to inject stub classes for unit testing.
class Parser extends \Eloquent {
public function __construct(Api $api) {
$this->api = $api;
}
public function Test() {
}
}
Be sure to run php artisan dump-autoload from the root of your project after adding new classes.
I am having an issue getting a Facade to work properly with a dependency injected into the underlying class.
I have a class called 'Listing'. It has one dependency called 'AdvertRepository' which is an interface and a class called EloquentAdvert which implements the interface. The code for these three classes is here:
// PlaneSaleing\Providers\Listing.php
<?php namespace PlaneSaleing\Providers;
use PlaneSaleing\Repositories\Advert\AdvertRepository;
class Listing {
protected $advert;
public function __construct (AdvertRepository $advert_repository) {
$this->advert = $advert_repository;
}
public function test() {
$this->advert->test();
}
public function test2() {
echo "this has worked";
}
}
// PlaneSaleing\Repositories\Advert\AdvertRepository.php
<?php namespace PlaneSaleing\Repositories\Advert;
interface AdvertRepository {
public function test();
}
// PlaneSaleing\Repositories\Advert\EloquentAdvert.php;
<?php namespace PlaneSaleing\Repositories\Advert;
class EloquentAdvert implements AdvertRepository {
public function test() {
echo 'this has worked';
}
}
I have then created a service provider called ListingServiceProvider.php, which has the following code:
// PlaneSaleing/Providers/ListingServiceProvider.php
<?php namespace PlaneSaleing\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\App;
class ListingServiceProvider extends ServiceProvider {
public function register() {
App::bind('PlaneSaleing\Repositories\Advert\AdvertRepository', 'PlaneSaleing\Repositories\Advert\EloquentAdvert');
}
}
I also added this to the ServiceProviders array in app.php
Now, if I inject Listing as a dependency into a controller and call the test method (as shown below) Laravel correctly detects the dependency, instantiates EloquentAdvert via its binding and displays 'this has worked'.
// Controllers/TestController.php
use PlaneSaleing\Providers\Listing;
class TestController extends BaseController {
protected $listing;
public function __construct(Listing $listing) {
$this->listing = $listing;
}
public function test1() {
$this->listing->test();
}
}
Now, I then created a facade for Listing. I added a new facade as follows and added an alias in app.php:
// PlaneSaleing\Providers\ListingFacade.php
<?php namespace PlaneSaleing\Providers;
use Illuminate\Support\Facades\Facade;
class ListingFacade extends Facade {
protected static function getFacadeAccessor() {
return 'Listing';
}
}
I also added the following new lines to ListingServiceProvider.php:
<?php namespace PlaneSaleing\Providers;
use PlaneSaleing\Repositories\Advert\AdvertRepository;
use PlaneSaleing\Repositories\Advert\EloquentAdvert;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\App;
class ListingServiceProvider extends ServiceProvider {
public function register() {
App::bind('PlaneSaleing\Repositories\Advert\AdvertRepository', 'PlaneSaleing\Repositories\Advert\EloquentAdvert');
// New lines...
$this->app['Listing'] = $this->app->share(function() {
return new Listing(new AdvertRepository);
});
}
}
NOW...if I call Listing::test(), I get the following error: Cannot instantiate interface PlaneSaleing\Repositories\Advert\AdvertRepository.
If I call Listing::test2() , I get 'this has worked' so it seems the Facade is working correctly.
It seems that when accessing Listing via its Facade the binding between AdvertRepository and EloquentAdvert doesnt work. I have looked at my code in the ServiceProvider thinking it was the issue, but I cant figure it out.
Both the Facade and binding work when tested individually but not when both are used at the same time.
Any ideas???
OK, So I have figured it out...For those who run into a similar problem...
The offending statement was in ListingServiceProvider.php which read:
$this->app['Listing'] = $this->app->share(function() {
return new Listing(new AdvertRepository);
});
The error is the new AdvertRepository statement. The reason being is that, we are telling php to directly instantiate the interface 'AdvertRepository'. Instead, we need to tell Laravel to instantiate the appropriate implementation of the 'AdvertRepository' interface. To do that, we use App::make('AdvertRepository'). That way, Laravel uses the binding previously declared to instantiate the correct implementation.
If your constructor is not being inject with a class, you must tell Laravel what class will be used when it needs to instantiate a particular interface:
Put this in your filters or bindings file:
App::bind('PlaneSaleing\Repositories\Advert\AdvertRepository', function()
{
return new PlaneSaleing\Repositories\Advert\EloquentAdvert;
});