I am trying to add some existing test cases to a pre existing project
Here is the API class
<?php
namespace MyApp\Api;
use MyApp\ApiBase;
use MyApp\Ethereum as Eth;
use PHPUnit\Runner\Exception;
/**
* Class Ethereum
* #package MyApp\Api
*/
class Ethereum extends ApiBase {
/**
* Function to get balances
* #param array $addresses
* #param string $tag
* #return array
*/
public function getBalances(array $addresses, string $tag = 'latest') {
$data = [];
foreach ($addresses as $addr) {
// validate address
if (!Eth::validateAddress($addr)) {
continue;
}
}
return $data;
}
}
The service class
<?php
namespace MyApp;
use MyApp\Ethereum\GethIpc;
use MyApp\Ethereum\GethWebsocket;
use PHPUnit\Runner\Exception;
/**
* Class Ethereum
* #package MyApp
*/
class Ethereum {
public static $subscriptions = [];
/**
* Ethereum constructor.
*/
public function __construct() {
$this->connection = new GethWebsocket();
$connect = $this->connection->connect();
}
/**
* Function to validate an address
* #param string $address
* #return bool
*/
public static function validateAddress(string $address) {
return preg_match("/^(0x)?[0-9a-fA-F]{40}$/", $address) !== 0;
}
}
My test class
<?php
declare(strict_types=1);
namespace MyApp\Test;
use MyApp\Api\Ethereum;
use MyApp\Ethereum as Eth;
use PHPUnit\Framework\TestCase;
use PHPUnit\Runner\Exception;
use Mockery;
use Mockery\Adapter\Phpunit\MockeryTestCase;
/**
* #covers MyApp\Api\Ethereum
*/
final class EthereumTest extends MockeryTestCase {
protected $ethereumApi;
protected $ethereum_address;
//Setup method called before every method
protected function setUp(): void {
$this->ethereumApi = new Ethereum();
$this->ethereum_address = '0x0000000000000000000000000000000' . rand(100000000, 999999999);
//Mockery::globalHelpers();
//$mock = mock(MyApp\Ethereum::class);
}
public function testGetBalances_ValidEthereumAddress(): void {
$mockEthereumService = Mockery::mock("Eth");
$mockEthereumService->shouldReceive('validateAddress')->once()->with($this->ethereum_address)->andReturn(true);
//$mockEthereumService->shouldReceive('msg')->once()->with($this->ethereum_address)->andReturn(true);
$addresses = [$this->ethereum_address];
$result = $this->ethereumApi->getBalances($addresses);
$this->assertNotEmpty($result);
}
public function tearDown()
{
Mockery::close();
}
}
Everytime I run the test class - the mock is not working and the actual service class method is being called
Can anyone offer assistance on how I should get this mock example working correctly?
Not having experience with Mockery myself, but after looking into the documentation
http://docs.mockery.io/en/latest/reference/creating_test_doubles.html#overloading
http://docs.mockery.io/en/latest/cookbook/mocking_hard_dependencies.html
I'd assume you need to use the "overload" prefix and the full classname and remove the use-statement for MyApp\Ethereum from your test case.
Related
I have an artisan command which gets some options, one of these options is --type=, like below:
protected $signature = 'make:procedure {name} {--type=}';
--type= contains the kind of difference, I want to check this option in the stub because each type has a different namespace which should be used in the stub.
for example, this is my stub:
<?php
namespace DummyNamespace;
class DummyClass
{
//
}
How can I do this, (of course this is an example, I just trying to explain my problem):
<?php
namespace DummyNamespace;
if ($type === 'one') {
echo 'use App\Some\Namespace\One'
}
class DummyClass
{
//
}
It would be highly appreciated if anyone can advise me!😊
your Custom command should derive from GeneratorCommand then you can use abstract Method getStub()
Your Stub File
namespace DummyNamespace;
/**
* Class DummyClass.
*/
class DummyClass
{
}
In Your Command File, you just need to use below code
/**
* Get the stub file for the generator.
*
* #return string
*/
protected function getStub()
{
return app_path('file/path/test.stub');
}
For Explanation Only
In GeneratorCommand class
/**
* Get the stub file for the generator.
*
* #return string
*/
abstract protected function getStub();
/**
* Build the class with the given name.
*
* #param string $name
* #return string
*
* #throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/
protected function buildClass($name)
{
$stub = $this->files->get($this->getStub());
return $this->replaceNamespace($stub, $name)->replaceClass($stub, $name);
}
A very simple way to generate files from stub.
In your stub file
namespace {{namespace}};
/**
* Class {{name}}.
*/
class {{name}}
{
}
Somewhere in your command
protected function getStub()
{
return file_get_contents(resource_path('stubs/dummy.stub'));
}
protected function generate($namespace, $name)
{
$template = str_replace(
['{{namespace}}', '{{name}}'],
[$namespace, $name],
$this->getStub()
);
file_put_contents(app_path("Dummies/$name.php"), $template);
}
I'm trying to write a unit test for the startedAt() method using mocks however the problem I'm facing is that I don't think I can access the builder instance from inside that startedAt() method.
To test the startedAt() method I created a fixture class called ExampleFilters and had it extend the parent class of Filters. Inside of the ExampleFilters class I import the FiltersByStartDate trait.
Does anyone have any suggestions on how I can access the builder property from the FiltersByStartDate trait?
Any ideas on this?
<?php
namespace App\Filters\Concerns;
trait FiltersByStartDate
{
/**
* Filter a query to include models of a specific date started.
*
* #param array $startedAt
* #return \Illuminate\Database\Eloquent\Builder
*/
public function startedAt($startedAt)
{
if (isset($startedAt[1])) {
$this->builder->whereHas('currentEmployment', function ($query) use ($startedAt) {
$query->whereBetween('started_at', [
$startedAt[0],
$startedAt[1]
]);
});
} else {
$this->builder->whereHas('currentEmployment', function ($query) use ($startedAt) {
$query->whereDate('started_at', $startedAt[0]);
});
}
return $this->builder;
}
}
<?php
namespace Tests\Fixtures;
use App\Filters\Concerns\FiltersByStartDate;
use App\Filters\Filters;
class ExampleFilters extends Filters
{
use FiltersByStartDate;
}
<?php
namespace App\Filters;
use Illuminate\Http\Request;
abstract class Filters
{
/**
* #var \Illuminate\Http\Request
*/
protected $request;
/**
* The Eloquent builder.
*
* #var \Illuminate\Database\Eloquent\Builder
*/
protected $builder;
/**
* Registered filters to operate upon
*
* #var array
*/
protected $filters = [];
/**
* Create a new class instance.
*
* #param \Illuminate\Http\Request $request
*/
public function __construct(Request $request)
{
$this->request = $request;
}
/**
* Apply the filters.
*
* #param \Illuminate\Database\Eloquent\Builder $builder
* #return \Illuminate\Database\Eloquent\Builder
*/
public function apply($builder)
{
$this->builder = $builder;
foreach ($this->getFilters() as $filter => $value) {
if (method_exists($this, $filter)) {
$this->$filter($value);
}
}
return $this->builder;
}
/**
* Fetch all relevant filters from the request.
*
* #return array
*/
public function getFilters()
{
return array_filter($this->request->only($this->filters));
}
}
<?php
namespace Tests\Unit\Filters\Concerns;
use Illuminate\Database\Query\Builder;
use Tests\Fixtures\ExampleFilters;
use Tests\TestCase;
/*
* #group filters
*/
class FiltersByStartDateTest extends TestCase
{
/* #var Tests\Fixtures\ExampleFilters */
protected $subject;
public function setUp(): void
{
$this->subject = app(ExampleFilters::class);
}
/** #test */
public function models_can_be_filtered_by_their_start_date()
{
// $this->markTestIncomplete();
$dateSet = ['2020-01-01 00:00:00'];
$mock = \Mockery::mock(Builder::class)
->shouldReceive('whereHas', \Mockery::any())
->shouldReceive('whereDate')
->withArgs(['started_at', $dateSet])
->once()
->andReturn(true)
->getMock();
dd($this->subject->startedAt($dateSet));
$builderMockFromDate = $this->subject->startedAt($dateSet);
$this->assertSame($builderMockFromDate, $mock);
}
}
I am trying to share variables on multiple views so i tried the view composer but it is not working as its not passing variables and is there any way to debug it
ComposerServiceProvider.php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class ComposerServiceProvider extends ServiceProvider
{
/**
* Bootstrap services.
*
* #return void
*/
public function boot()
{
view()->composer(
'layouts.admin.dashboard',
'App\Http\ViewComposers\StatComposer'
);
}
/**
* Register services.
*
* #return void
*/
public function register()
{
//
}
}
So now here will be the Composer File
StatComposer.php
<?php
namespace App\Http\ViewComposers;
use Analytics;
use Spatie\Analytics\Period;
use App\Libraries\GoogleAnalytics;
use Illuminate\Http\Request;
use Illuminate\View\View;
class StatComposer
{
/**
* Create a movie composer.
*
* #return void
*/
public function __construct()
{
$result = GoogleAnalytics::topCountries();
$country = $result->pluck('country');
$country_sessions = $result->pluck('sessions');
$topBrowsers = GoogleAnalytics::topBrowsers();
$browser = $topBrowsers->pluck('browser');
$browser_sessions = $topBrowsers->pluck('sessions');
$totalPageViews = GoogleAnalytics::fetchVisitorsAndPageViews();
$date = $totalPageViews->pluck('date');
$visitors = $totalPageViews->pluck('visitors');
$pageViews = $totalPageViews->pluck('pageViews');
}
/**
* Bind data to the view.
*
* #param View $view
* #return void
*/
public function compose(View $view)
{
$view->with('country', 'country_sessions', 'browser', 'browser_sessions','date','visitors','pageViews');
}
}
So I am unable to find way to debug it as the variables are not passing to the view i am trying to pass them to and the view is giving error of undefined variable.
The issue is your declaring a lot of variables without providing the values in the compose function. Also your not actually saving the data anywhere. It should be like this.
protected $country;
/**
* Create a movie composer.
*
* #return void
*/
public function __construct()
{
$result = GoogleAnalytics::topCountries();
$this->country = $result->pluck('country');
}
/**
* Bind data to the view.
*
* #param View $view
* #return void
*/
public function compose(View $view)
{
$view->with('country', $this->country);
}
Note we are declaring the classes property at the top, then saving to that property and then setting the 'country' variable in the with method with the $this->country property.
I have the following phpunit test for testing the controllers:
The DefaultControllerTest:
namespace Tests\AppBundle\Controller;
use Tests\AppBundle\Controller\BasicHttpController;
use AppBundle\DataFixtures\Test\DummyUserFixtures;
/**
* #testtype Functional
*/
class DefaultControllerTest extends BasicHttpController
{
/**
* {#inheritdoc}
*/
public function setUp()
{
$fixture = new DummyUserFixtures();
$fixture->load($this->entityManager);
}
/**
* Testing the Behavior when visiting the index page
*/
public function testIndex()
{
$client = $this->client;
$router=$client->getContainer()->get('router');
$crawler = $client->request('GET', '/');
$response=$client->getResponse();
$this->assertTrue($client->getResponse()->isRedirect());
$this->assertEquals($router->getRouteCollection()->get('fos_user_security_login')->getPath(),$response->headers->get('Location'));
//#todo Create Dummy Users
// $this->checkPanelAfterSucessfullLogin($crawler);
}
}
That extends the following test BasicHttpController (try to apply the DRY principle):
namespace Tests\AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
class BasicHttpController extends WebTestCase
{
protected $entityManager=null;
protected $client=null;
/**
* {#inheritdoc}
*/
public function __construct()
{
parent::__construct();
$this->client = static::createClient();
$container = $this->client->getContainer();
$doctrine = $container->get('doctrine');
$this->entityManager=$doctrine->getManager();
}
/**
* Remove all entities from the database
*/
protected function truncateEntities()
{
$purger = new ORMPurger($this->entityManager());
$purger->purge();
}
/**
* {#inheritdoc}
*/
public function tearDown()
{
$this->truncateEntities();
}
/**
* #param username String the user's username
* #param passwoρd String the user's password
*/
protected function checkPanelAfterSucessfullLogin($crawler,string $username,string $password)
{
//Submitting the form
$form=$crawler->selectButton('_submit')->form();
$form['_username']=$username;
$form['_password']=$password;
$crawler=$crawler->submit($form);
$response=$client->getResponse();
$this->assertTrue($client->getResponse()->isRedirect());
$client->followRedirect();
//Checking header
$headerDom=$crawler->filter('header')->childen()->filter('nav.navbar')->children();
$this->assertCount(1,$headerDom->find('a.navbar-brand')); //homepage link
$this->assertCount(1,$headerDom->find('a.btn-danger')); //Logout button
}
}
As you can see I try to load the following fixture:
namespace AppBundle\DataFixtures\Test;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Doctrine\Common\Persistence\ObjectManager;
class DummyUserFixtures extends AbstractFixture implements OrderedFixtureInterface,ContainerAwareInterface
{
/**
* #var ContainerInterface
*/
private $container=null;
/**
* {#inheritDoc}
*/
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
/**
* Generic function that creates a user with provided information.
* #param $name {String} The user's name
* #param $surname {String} The user's surname
* #param $username {String} The user's username
* #param $password {String} The user's password
* #param $email {String} The user's recovery email
* #param $role {String} The user's system role
* #param $phone {String | null} The user's phone number
* #param $organization {String|null} The user's organization
* #param $occupation {String|null} The user's occupation
*
* #return AppBundle\Entity\User
*/
private function createUser($name,$surname,$username,$password,$email,$role,$phone=null,$organization=null,$occupation=null)
{
$fosUserManager=$this->container->get('fos_user.user_manager');
/**
* #var AppBundle\Entity\User
*/
$user=$fosUserManager->createUser();
$user->setUsername($username);
$user->setEmail($email);
$user->setPlainPassword($password);
$user->setEnabled(true);
$user->setRoles(array($role));
$user->setName($name);
$user->setSurname($surname);
if($phone){
$user->setPhone($phone);
}
if($organization){
$user->setOrganization($organization);
}
if($occupation){
$user->setOccupation($occupation);
}
$fosUserManager->updateUser($user, true);
return $user;
}
/**
* {#inheritDoc}
*/
public function load(ObjectManager $manager)
{
$this->createUser('John','Doe','jdoe','simplepasswd','jdoe#example.com','ROLE_USER','+3021456742324','Acme Products','Soft Engineer');
$this->createUser('Jackie','Chan','jchan','thesimplepasswd','jackiechan#example.com','ROLE_ADMIN','+302141232324','Holywood','Actor');
$this->createUser('Chuck','Norris','chuck_norris','unhackablepasswd','chucknorris#example.com','ROLE_SUPERADMIN',null,'Universe','Master');
}
public function getOrder()
{
return 1;
}
}
But for some reason I get the following error:
There was 1 error:
1) Tests\AppBundle\Controller\DefaultControllerTest::testIndex
Error: Call to a member function get() on null
/home/vagrant/code/src/AppBundle/DataFixtures/Test/DummyUserFixtures.php:50
/home/vagrant/code/src/AppBundle/DataFixtures/Test/DummyUserFixtures.php:87
/home/vagrant/code/tests/AppBundle/Controller/DefaultControllerTest.php:19
Further debugging has proved that the error is triggered by the following line in DummyUserFixtures:
$fosUserManager=$this->container->get('fos_user.user_manager');
So do you know how to load the data via fixtures?
In order to get it working you should set the service container you generate from the static::createClient() method and pass it via the $fixture->setContainer($container)
So a good approach is to define the container as protected instance variable to the BasicHttpController so any Test class (eg. the DefaultControllerTest in your case) is able to load the fixtures accordingly.
So using the setUp method and instance variables of BasicHttpController should be the following:
//Namespace declaration goes there
class BasicHttpController extends WebTestCase
{
protected $entityManager=null;
protected $client=null;
protected $container=null;
/**
* {#inheritdoc}
*/
public function setUp()
{
$this->client = static::createClient();
$this->container = $this->client->getContainer();
$doctrine = $this->container->get('doctrine');
$this->entityManager=$doctrine->getManager();
}
// Rest methods here
}
Note: on classes that are getting inherited from BasicHttpController you can define the setUp like that:
public function setUp()
{
parent::setUp();
// Add extra stuff here
}
So you can do more setUp bootstrapping before tests.
I found some strange behavior with mockup builder, can someone explain to me why this happen?
here is my test code:
class PlaceTest extends \PHPUnit_Framework_TestCase
{
const API_KEY = 'test-api';
public function testConstruct()
{
$google = $this->getMockBuilder('GusDeCooL\GooglePhp\Google')
->setConstructorArgs(array(self::API_KEY))
->setMethods(array('getKey'))
->getMock();
$google->expects($this->any())
->method('getKey')
->will($this->returnValue(self::API_KEY));
/* #var $google \GusDeCooL\GooglePhp\Google */
$place = new Place($google);
$this->assertInstanceOf('GusDeCooL\GooglePhp\Component\Place\Place', $place);
$this->assertInstanceOf('GusDeCooL\GooglePhp\Google', $place->getParent());
$this->assertEquals(self::API_KEY, $place->getKey());
return $place;
}
/**
* #param Place $place
*
* #depends testConstruct
*/
public function testGetKey(Place $place)
{
$this->assertInstanceOf('GusDeCooL\GooglePhp\Google', $place->getParent());
$this->assertEquals(self::API_KEY, $place->getKey());
}
}
And here is the code of actual class
<?php
namespace GusDeCooL\GooglePhp\Component\Place;
use GusDeCooL\GooglePhp\Component\ChildInterface;
use GusDeCooL\GooglePhp\Google;
use GusDeCooL\GooglePhp\Place\Nearby;
class Place implements ChildInterface
{
/**
* #var Google
*/
private $parent;
/**
* #var Nearby
*/
private $nearby;
public function __construct(Google $parent)
{
$this->setParent($parent);
}
/**
* API Key
* #return string
*/
public function getKey()
{
return $this->getParent()->getKey();
}
}
While running the test, the PlaceTest::testConstruct() while doing $place->getKey() it pass the test but it errors in PlaceTest::testGetKey()
How is that happen?
It seems this is the limitation of mockup builder.
http://phpunit.de/manual/3.7/en/test-doubles.html
we can't pass mockup object into another test scope.
Please leave a comment if you found another reason.