I am new to PHPUnit and am trying to set up my first test. It is failing with the following error message
Failed asserting that ReflectionProperty Object (...) is an instance of class "Illuminate\Database\Eloquent\Model".
Essentially, I am trying to mock the creation of a model which I am injecting into a constructor and testing it is the right type through reflection. I am following the advice laid out in this post/answer:
How do I test this class using phpunit?
If anyone can give me some guidance, that would greatly appreciated.
The class being tested is here:
<?php
namespace PlaneSaleing\Repo\Listing;
use Illuminate\Database\Eloquent\Model;
class EloquentListing implements ListingInterface {
protected $advert;
public function __construct(Model $advert)
{
$this->advert = $advert;
}
/**
* Get paginated listings
*
* #param int Current page
* #param int Number of listings per page
* #return StdClass object with $items and $totalItems for pagination
*/
public function byPage($page=1, $limit=10)
{
$result = new \StdClass;
$result->page = $page;
$result->limit = $limit;
$result->totalItems = 0;
$result->items = array();
$listings = $this->advert
->orderBy('created_at')
->skip( $limit * ($page-1) )
->take($limit)
->get();
// Create object to return data useful for pagination
$result->items = $listings->all();
$result->totalItems = $this->totalListings();
return $result;
}
The service provider is here:
<?php
namespace PlaneSaleing\Repo;
use Illuminate\Support\ServiceProvider;
use PlaneSaleing\Repo\Listing\EloquentListing as Listing;
use \Advert;
class RepoServiceProvider extends ServiceProvider {
public function register()
{
$this->app->bind('PlaneSaleing\Repo\Listing\ListingInterface', function($app) {
$app->make(Listing(new Advert));
} );
}
}
My Test is here:
<?php
use \Mockery;
use \ReflectionClass;
use PlaneSaleing\Repo\Listing\EloquentListing;
class EloquentListingTest extends \TestCase
{
/**
* Testing if __constructor is setting up property
*/
public function testModelSetsUp()
{
$mock1 = Mockery::mock(Illuminate\Database\Eloquent\Model::class);
$listing = new EloquentListing($mock1);
$reflection = new ReflectionClass($listing);
// Making your attribute accessible
$property1 = $reflection->getProperty('advert');
$property1->setAccessible(true);
$this->assertInstanceOf(Illuminate\Database\Eloquent\Model::class, $property1);
}
When you assert, you should use the actual value of the reflection property, not the property itself:
public function testModelSetsUp()
{
$mock1 = Mockery::mock(Illuminate\Database\Eloquent\Model::class);
$listing = new EloquentListing($mock1);
$reflection = new ReflectionClass($listing);
// Making your attribute accessible
$property1 = $reflection->getProperty('advert');
$property1->setAccessible(true);
$this->assertInstanceOf(
Illuminate\Database\Eloquent\Model::class,
$property1->getValue($listing)
);
}
However, you can simplify the test a lot:
public function testModelSetsUp()
{
$mock1 = Mockery::mock(Illuminate\Database\Eloquent\Model::class);
$listing = new EloquentListing($mock1);
$this->assertAttributeInstanceOf(
Illuminate\Database\Eloquent\Model::class,
'advert',
$listing
);
}
Even better, you could use meaningful variable names, use a meaningful test method name, and not just assert that the $advert property is an instance of Illuminate\Database\Eloquent\Model, but actually the same instance as passed into the constructor:
public function testConstructorSetsAdvert()
{
$advert = Mockery::mock(Illuminate\Database\Eloquent\Model::class);
$listing = new EloquentListing($advert);
$this->assertAttributeSame(
$advert,
'advert',
$listing
);
}
Related
I have a unit test class in which I want to instantiate a object from another class in order to that I used setUpBeforeClass() fixtures of phpunit. So if I will use that recently instantiated object directly in test function then its working fine.
If i'll use this object into another function which had been created for data providers. So that object sets to null cause providers always execute first.
Is there a way to call dataProviders just before the test runs, instead?
require_once('Dashboard.php');
Class Someclass extends PHPUnit_Framework_TestCase {
protected static $_dashboard;
public static function setUpBeforeClass()
{
self::$_dashboard = new Dashboard();
self::$_dashboard->set_class_type('Member');
}
/**
* Test Org Thumb Image Existense
* param org profile image : array
* #dataProvider getOrgProfileImages
*/
public function testFieldValidation($a,$b){
//If I call that object function here it will give the result.
//$members = self::$_dashboard->get_members();
//var_dump($members); Printing result as expected
$this->assertTrue(true);
}
public function getOrgProfileImages() : array {
//var_dump(self::$_dashboard);
$members = self::$_dashboard->get_members();
$tmp_array = ['2','2'];
return $tmp_array;
}
public static function tearDownAfterClass()
{
self::$_dashboard = null;
}
}
Error:
The data provider specified for Someclass::testFieldValidation is invalid.
Call to a member function get_members() on null
Please help to mitigate this issue.
Note: since I don't have the source of your Dashboard class, I'm using a random number in the examples below instead
Providers are invoked before any tests are run (and before any hooks, including beforeClass have a chance to run). By far the easiest way to achieve what you're after is to populate that static property on the class load:
use PHPUnit\Framework\TestCase;
/** #runTestsInSeparateProcesses enabled */
class SomeTest extends TestCase
{
public static $_rand = null;
public function provider()
{
$rand = self::$_rand;
var_dump(__METHOD__, getmypid(), 'provided rand', $rand);
return ['rand' => [$rand]];
}
/** #dataProvider provider */
public function testSomething($rand)
{
$this->expectNotToPerformAssertions();
var_dump(__METHOD__, getmypid(), 'tested with', $rand);
}
/** #dataProvider provider */
public function testSomethingElse($rand)
{
$this->expectNotToPerformAssertions();
var_dump(__METHOD__, getmypid(), 'tested with', $rand);
}
}
// this runs before anything happens to the test case class
// even before providers are invoked
SomeTest::$_rand = rand();
Or you could instantiate you dashboard in the provider itself, on the first call:
public function provider()
{
// Instantiate once
if (null === self::$_rand) {
self::$_rand = rand();
}
$rand = self::$_rand;
var_dump(__METHOD__, getmypid(), 'provided rand', $rand);
return ['rand' => [$rand]];
}
#dirk-scholten is right. You SHOULD be creating a new object for each test. It's a GOOD testing practice. Frankly it looks more like you are testing the data and not testing the code, which is fine I guess, it's just not the typical use of PHPUnit. Based on the assumption that you want to make sure every user in the database has a thumbnail image (just guessing), I would go with the following:
<?php
class DashboardDataTest extends PHPUnit\Framework\TestCase {
private $dashboard;
public function setUp() {
$this->dashboard = new Dashboard();
}
/**
* Test Org Thumb Image Existence
* param org profile image : array
*
* #dataProvider getOrgProfileImages
*
* #param int $user_id
*/
public function testThumbnailImageExists(int $user_id){
$thumbnail = $this->dashboard->get_member_thumbnail($user_id);
$this->assertNotNull($thumbnail);
}
public function geOrgUserIDs() : array {
$dashboard = new Dashboard();
// Something that is slow
$user_ids = $dashboard->get_all_the_member_user_ids();
$data = [];
foreach($user_ids as $user_id){
$data[] = [$user_id];
}
return $data;
}
}
Each data provider will get called once and only once before the tests. You do not need a static data fixture on the class because phpunit handles the data fixture for you when you use data providers.
Let's say I have Vehicle model (it's Eloquent model) that stores different types of vehicles (in vehicles table). Of course, there are many different types of vehicles, so I have for example:
class Car extends Vehicle {
}
class Bicycle extends Vehicle {
}
and so on.
now I need to find object based on vehicle and here's the problem. I've added the following method in Vehicle model:
public function getClass()
{
return __NAMESPACE__ . '\\' . ucfirst($this->type)
}
so I can find the class name I should use.
But the only way to get valid class is like this:
$vehicle = Vehicle::findOrFail($vehicleId);
$vehicle = ($vehicle->getClass())::find($vehicleId);
which is not the best solution because I need to run 2 exact same queries to get valid final class object.
Is there any way to achieve same without duplicating the query?
An alternative to #jedrzej.kurylo's method would be to just override one method in your Vehicle class:
public static function hydrate(array $items, $connection = null)
{
$models = parent::hydrate($items, $connection);
return $models->map(function ($model) {
$class = $model->getClass();
$new = (new $class())->setRawAttributes($model->getOriginal(), true);
$new->exists = true;
return $new;
});
}
Hope this helps!
In order for Eloquent to correctly return objects of a class determined by the type column, you'll need to override 2 methods in your Vehicle model class:
public function newInstance($attributes = array(), $exists = false)
{
if (!isset($attributes['type'])) {
return parent::newInstance($attributes, $exists);
}
$class = __NAMESPACE__ . '\\' . ucfirst($attributes['type']);
$model = new $class((array)$attributes);
$model->exists = $exists;
return $model;
}
public function newFromBuilder($attributes = array(), $connection = null)
{
if (!isset($attributes->type)) {
return parent::newFromBuilder($attributes, $connection);
}
$instance = $this->newInstance(array_only((array)$attributes, ['type']), true);
$instance->setRawAttributes((array)$attributes, true);
return $instance;
}
For anybody else that comes across this page, this is what worked for me. I copied the newInstance and newFromBuilder from the source code, and put them in my parent class, in this case it would be Vehicle.
I think the newInstance method is ran twice when building up a query builder instance. In the newInstance method I would check if the type is set in the attributes, and if so then get the namespace based off the type (I used PHP Enums). On the second pass $attributes gets converted to an object rather than array, not sure why but don't worry about your IDE complaining.
In the newFromBuilder method I had to pass $attributes in to the newInstance method, as before it was just passing an empty array.
$model = $this->newInstance([], true);
to:
$model = $this->newInstance($attributes, true);
Vehicle.php
/**
* Create a new instance of the given model.
*
* #param array $attributes
* #param bool $exists
* #return static
*/
public function newInstance($attributes = [], $exists = false)
{
// This method just provides a convenient way for us to generate fresh model
// instances of this current model. It is particularly useful during the
// hydration of new objects via the Eloquent query builder instances.
$model = new static;
if (isset($attributes->type)) {
$class = // Logic for getting namespace
$model = new $class;
}
$model->exists = $exists;
$model->setConnection(
$this->getConnectionName()
);
$model->setTable($this->getTable());
$model->mergeCasts($this->casts);
$model->fill((array) $attributes);
return $model;
}
/**
* Create a new model instance that is existing.
*
* #param array $attributes
* #param string|null $connection
* #return static
*/
public function newFromBuilder($attributes = [], $connection = null)
{
// I had to pass $attributes in to newInstance
$model = $this->newInstance($attributes, true);
$model->setRawAttributes((array) $attributes, true);
$model->setConnection($connection ?: $this->getConnectionName());
$model->fireModelEvent('retrieved', false);
return $model;
}
By making these changes I could do Vehicle::all() and get a collection containing both Car and Bicycle classes.
I'm developing an Apigility driven application based on the Zend Framework 2.
Currently I'm sending the data retrieved in the database directly to the client: a request comes in, the MyResource#fetch(...) or MyResource#fetchAll(...) gets triggered and calls an appropriate method on MyService class, that calls MyMapper to retireve the data with its methods like findFooByBar(...).
Now I'd like to process the data, before the response is sent. How can I do that?
The Apigility ZF HAL documentation shows, how to access the entity data between it has been retrieved and sent to the client. Well I tried this out. It's ugly and to much code for such task. And... it doesn't work. I want however post here my attept:
namespace Portfolio;
...
class Module implements ApigilityProviderInterface {
private $serviceManager;
public function onBootstrap(MvcEvent $event) {
$application = $event->getTarget();
$this->serviceManager = $serviceManager = $application->getServiceManager();
$viewHelperManager = $serviceManager->get('ViewHelperManager');
$hal = $viewHelperManager->get('Hal');
$hal->getEventManager()->attach('renderEntity', array($this, 'onRenderEntity'));
$hal->getEventManager()->attach('renderCollection', array($this, 'onRenderCollection'));
}
public function onRenderEntity($event) {
$entity = $event->getParam('entity');
if ($entity->entity instanceof ProjectEntity) {
$projectEntity = $entity->entity;
$imageCollection = $this->tempCreateimagesForProject(
$event, $entity->entity->getId()
);
$projectEntity->setImages($imageCollection);
$event->setParam('entity', $projectEntity);
}
}
public function onRenderCollection($event) {
$collection = $event->getParam('collection');
$projectCollection = $collection->getCollection();
if ($projectCollection instanceof ProjectCollection) {
foreach ($projectCollection as $key => $projectItem) {
$tempProject = $projectCollection->getItem($key);
$tempProject->append(
['images' => $this->tempCreateimagesForProject($tempProject->offsetGet('id'))]
);
$projectCollection->getItem($key)->offsetSet($key, $tempProject);
}
}
}
private function tempCreateimagesForProject(Event $event, $projectId) {
$imageService = $this->serviceManager->get('Portfolio\V2\Rest\ImageService');
$imageCollection = $imageService->getImagesForProject($projectId);
return $imageCollection;
}
...
}
I think using the renderEntity and renderCollection events is not the correct spot to add this kind of resource specific logic. It is more suitable for more general changes or incidental customization.
You can add this logic to your resource listeners. So in your fetch and fetchAll methods in your MyResource class you can add the custom code you currently added in these onRenderEntity and onRenderCollection methods.
So something like this:
class MyResource extends AbstractResourceListener
{
/**
* Your image service dependency
*/
protected $imageService;
/* ... */
public function fetch($id)
{
$project = $this->projectMapper->fetch($id);
$imageCollection = $this->imageService->getImagesForProject($project);
$project->setImages($imageCollection);
return $project;
}
/* ... */
public function fetchAll($params = array())
{
$projects = $this->projectMapper->fetchAll();
foreach ($projects as $key => $project) {
$imageCollection = $this->imageService->getImagesForProject($project);
$project->setImages($imageCollection);
}
return $projects;
}
/* ... */
}
One possible solution is handling the data in the Hydrator. So we write a custom Hydrator class and enrich the items with nested objects and lists in it. It can look like this:
Portfolio\V2\Rest\Project\ProjectHydrator
...
class ProjectHydrator extends ClassMethods {
/**
* #var ImageService
*/
protected $imageService;
...
/*
* Doesn't need to be implemented:
* the ClassMethods#hydrate(...) handle the $data already as wished.
*/
/*
public function hydrate(array $data, $object) {
$object = parent::hydrate($data, $object);
if ($object->getId() !== null) {
$images = $this->imageService->getImagesForProject($object->getId());
$object->setImages($images);
}
return $object;
}
*/
/**
* #see \Zend\Stdlib\Hydrator\ClassMethods::extract()
*/
public function extract($object) {
$array = parent::extract($object);
if ($array['id'] !== null) {
$images = $this->imageService->getImagesForProject($array['id']);
$array['images'] = $images;
}
return $array;
}
}
It's not a nice solution, since then a part of the model / data retrieving logic gets moved to the hydrator. But it works. Here is shown an implementation of this approach and here is a discussion to this topic on GitHub.
If you are using the ClassMethods Hydrator and your Collection extends \Zend\Paginator\Paginator a good solution without losing the Collection's consistency and not changing anybody's code is to overwrite your getCurrentItems() method.
public class MyResourceCollection // most likely extends Paginator
{
public function getCurrentItems()
{
// getting the original items
$items = parent::getCurrentItems();
// as we work with objects $item will be an object
// when working with objects we use references to them not clones of objects
// so changes to $item are also present in the collection
foreach ($collection as $item) {
$oldSomething = $item->getSomething();
$item->setSomething('['.$oldSomething.']');
}
// $items are now changed, return them
return $items;
}
}
I have named the key something not to get confused with the getValue method from other places.
This makes the something value look like [something].
There is a shortcut method to create an object from a method that return a string?
For the moment, I used that :
class MyClass {
/**
* #return string
*/
public function getEntityName() {
return 'myEntityName';
}
}
$myClassInstance = new MyClass();
// Need to get string
$entityName = $myclassInstance->getEntityName();
// And after I can instantiate it
$entity = new $entityName();
There is short-cut syntax for getting the string but not for creating the object from the string to date in PHP. See the following code in which I also include a 'myEntityName' class:
<?php
class myEntityName {
public function __construct(){
echo "Greetings from " . __CLASS__,"\n";
}
}
class MyClass {
/**
* #return string
*/
public function getEntityName() {
return 'myEntityName';
}
}
$entityName = ( new MyClass() )->getEntityName();
$entity = new $entityName();
With one line the code instantiates a MyClass object and executes its getEntityName method which returns the string $entityName. Interestingly, if I replace my one-liner with the following it fails in all versions of PHP except for the HipHop Virtual Machine (hhvm-3.0.1 - 3.4.0):
$entityName = new ( ( new MyClass() )->getEntityName() );
I'm trying to decide whether to create many classes for each content type I have in my application/database or just stick with procedural code.
Version 1:
make a class for each object collection:
class App{
protected $user_collection;
function getUserCollection(){
if(!isset($this->user_collection)
$this->user_collection = new UserCollection($this);
return $this->user_collection;
}
// ...
}
class UserCollection{
function __construct(App $app){
$this->app = $app;
}
function getUser($user){
return new User($this->app, $user);
}
function getUsers($options){
$users = $this->app->getDatabase()->query($options);
foreach($users as &$user)
$user = new User($this, $user);
return $users;
}
// ...
}
which I'm using like:
$app = new App();
echo $app->getUserCollection()->getUser('admin')->email_address;
version 2:
keep all methods in a single class
class App{
function getUsers($options){
$users = $this->getDatabase()->query($options);
foreach($users as &$user)
$user = new User($this, $user);
return $users;
}
function getUser($user){
return new User($this, $user);
}
// ...
}
used like:
$app = new App();
echo $app->getUser('admin')->email_address;
version 3:
make getUsers() a a static method in the "User" class (the method instantiates a new User object):
$app = new App();
echo User::getUser($app, 'admin')->email_address;
Which way should I go? The "user" object is just an example, App has other objects too, like "database", "pages" etc.
I would use your version 1, but I would make getUser() and getUsers() methods of App.
This gets rid of the awkward getUserCollection() call, because instead inside the getUser() and what not you just call $this->user_collection.
Personnaly, I often used the second one with method like this:
class user {
/**
* Load object from ...
*/
public function load($userId) {}
/**
* Insert or Update the current object
*/
public function save() {}
/**
* Delete the current object
*/
public function delete() {
// delete object
// Reset ID for a future save
$this->UserID = null;
}
/**
* Get a list of object
*/
public static function getList() {
// Make your search here (from DB)
// Put rows into new "SELF" object
$list = array();
foreach($rows as $row) {
$obj = new self();
$obj->populate($row);
$list[$obj->UserID] = $obj; // Associative array or not...
}
}
}
Like you can see, I set my "getList" function static to simply access like this:
$listUsers = user::getList();
OK, it's very simple but work in most case of simple app.