I'm going through the zend tutorials and I am testing a class with a mock object with phpunit. When I pass a mock created from Zend\Db\TableGateway to my class, who's constructor expects a Zend\Db\TableGateway, I get an type error:
"...Argument 1 passed to Album\Model\AlbumTable::__construct() must be an instance of Zend\Db\TableGateway\TableGateway, instance of Mock_TableGateway_65b55cb0 given..."
Is this supposed happen? Are phpunit mock objects supposed to be able to "fool" the class?
Here is the real class:
class AlbumTable {
protected $tableGateway;
public function __construct(TableGateway $tableGateway) {
$this->tableGateway = $tableGateway;
}
public function fetchAll() {
$resultSet = $this->tableGateway->select();
return $resultSet;
}
public function getAlbum($id){
$id = (int) $id;
$rowset = $this->tableGateway->select(array('id' => $id));
$row = $rowset->current();
if(!$row) {
throw new \Exception("Couldn't find row: $id");
}
return $row;
}
public function saveAlbum(Album $album) {
$data = array(
'artist' => $album->artist,
'title' => $album->title,
);
$id = (int)$album->id;
if ($id == 0) {
$this->tableGateway->insert($data);
} else {
if ($this->getAlbum($id)) {
$this->tableGateway->update($data, array('id' => $id));
} else {
throw new \Exception('Form id does not exist');
}
}
}
public function deleteAlbum($id) {
$this->tableGateway->delete(array('id' => $id));
}
}
and the test:
class AlbumTableTest extends PHPUnit_Framework_TestCase {
public function testFetchAllReturnsAllAlbums() {
$resultSet = new ResultSet();
$mockTableGateway = $this->getMock('Zend\Db\TableGateway',
array('select'), array(), '', false);
$mockTableGateway->expects($this->once())
->method('select')
->with()
->will($this->returnValue($resultSet));
$albumTable = new AlbumTable($mockTableGateway);
$this->assertSame($resultSet, $albumTable->fechAll());
}
}
and the error:
Time: 102 ms, Memory: 5.00Mb
There was 1 error:
1) AlbumTest\Model\AlbumTableTest::testFetchAllReturnsAllAlbums
Argument 1 passed to Album\Model\AlbumTable::__construct() must be an instance of Zend\Db\TableGateway\TableGateway, instance of Mock_TableGateway_65b55cb0 given, called in C:\Users\MEEE\Google Drive\code\iis\www\CommunicationApp\module\Album\test\AlbumTest\Model\AlbumTableTest.php on line 20 and defined
C:\Users\MEEE\Google Drive\code\iis\www\CommunicationApp\module\Album\src\Album\Model\AlbumTable.php:9
C:\Users\MEEE\Google Drive\code\iis\www\CommunicationApp\module\Album\test\AlbumTest\Model\AlbumTableTest.php:20
FAILURES!
Tests: 4, Assertions: 9, Errors: 1.
You are not mocking the correct class. You are creating a mock of a Zend\Db\TableGateway and you need to actually mock Zend\Db\TableGateway\TableGateway
Change you test code to:
$mockTableGateway = $this->getMock('Zend\Db\TableGateway\TableGateway',
array('select'), array(), '', false);
Your mock was failing a type-hint because your not mocking the correct class.
Mock objects will extend the class that you are mocking, so they will be an instance of the class being mocked.
Related
I'm trying to make a use from Steam API data as I like to learn on live examples, and looking at the way various statistics are returned I began to think that OOP approach would suit me best in this case.
What I'm trying to achieve is to loop through all the results, and programatically populate an array with objects of type that corresponds to the actual type of the statistic. I've tried to build myself a basic class, called Statistic, and after instantiating an object determine wheter or not it's class should change (i.e. whether or not to cast an object of type that Statistic is parent to and if so, of what type). How to do that in PHP? My solution gives me no luck, all of the objects are of type Statistic with it's 'type' property being the object I want to store alone in the array. Code:
$data = file_get_contents($url);
$data = json_decode($data);
$data = $data->playerstats;
$data = $data->stats;
$array;
for($i=0;$i<165;$i++)
{
$array[$i] = new Statistic($data[$i]);
echo "<br/>";
}
var_dump($array[10]);
And the classes' code:
<?php
class Statistic
{
public function getProperties()
{
$array["name"] = $this->name;
$array["value"] = $this->value;
$array["type"] = $this->type;
$array["className"] = __CLASS__;
return json_encode($array);
}
public function setType($x)
{
$y = explode("_",$x->name);
if($y[0]=="total")
{
if(!isset($y[2]))
{
$this->type = "General";
}
else
{
if($y[1]=="wins")
{
$this->type = new Map($x);
$this->__deconstruct();
}
if($y[1]=="kills")
{
$this->type = new Weapon($x);
$this->__deconstruct();
}
else $this->type="Other";
}
}
else $this->type = "Other";
}
function __construct($obj)
{
$this->name = $obj->name;
$this->value = $obj->value;
$this->setType($obj);
}
function __deconstruct()
{
echo "deconstructing <br/>";
return $this->type;
}
}
class Weapon extends Statistic
{
public function setType($x)
{
$y = explode("_",$x);
if($y[1]=="kills")
{
$this->type = "kills";
}
else if($y[1]=="shots")
{
$this->type = "shots";
}
else if($y[1]=="hits")
{
$this->type = "hits";
}
}
function __construct($x)
{
$name = explode("_",$x->name);
$this->name = $name[2];
$this->value = $x->value;
$this->setType($x->name);
}
function __deconstruct()
{
}
}
class Map extends Statistic
{
public function setType($x)
{
if($x[1]=="wins")
{
$this->type = "wins";
}
if($x[1]=="rounds")
{
$this->type = "rounds";
}
}
public function setName($name)
{
if(isset($name[3]))
{
if(isset($name[4]))
{
return $name[3] + " " + $name[4];
}
else return $name[3];
}
else return $name[2];
}
function __construct($x)
{
$name = explode("_",$x->name);
$this->name = $this->setName($name);
$this->value = $x->value;
$this->setType($name);
}
function __deconstruct()
{
}
}
Gives the result:
object(Statistic)#223 (3) {
["name"]=> string(18) "total_kills_deagle"
["value"]=> int(33)
["type"]=> object(Weapon)#222 (3) {
["name"]=> string(6) "deagle"
["value"]=> int(33)
["type"]=> string(5) "kills" }
}
Should that determination be driven from the loop itself, the whole advantage of having a set of functions that does everything for me and returns a ready-to-serve data is gone, since I would really have to cast different objects that aren't connected to each other, which is not the case here. How can I achieve returning objects of different type than the object itself is?
For answer your question How can I achieve returning objects of different type than the object itself is?
"Casting to change the object's type is not possible in PHP (without using a nasty extension)"
For more info: Cast the current object ($this) to a descendent class
So you can't change the class type of an instance with type of a derived class. In other world can't change instance of Static with instance of Weapon.
I am using version v1.4.2.18. The library can be found here: https://github.com/Youshido/GraphQL
I am trying to accomplish the following:
query {
articleSummary(id:1) {
title,
body,
article {
id
}
}
}
I have an ArticleSummaryField.php:
class ArticleSummaryField extends AbstractField
{
public function build(FieldConfig $config)
{
$config->addArgument('id', new NonNullType(new StringType()));
}
public function getType()
{
return new ArticleSummaryType();
}
public function resolve($value, array $args, ResolveInfo $info)
{
return [
'title' => 'test title',
'body' => 'test body',
'article' => $args['id']
];
}
}
Then the ArticleSummaryType.php:
class ArticleSummaryType extends AbstractObjectType
{
public function build($config)
{
$config
->addField('title', new StringType());
->addField('body', new StringType());
->addField('article', new ArticleField());
}
}
Then the ArticleField.php has the getType method return the ArticleType which has the id field.
However what i am getting is an error:
Fatal error: Uncaught Error: Call to undefined method ArticleField::getNullableType() in .../vendor/youshido/graphql/src/Execution/Processor.php on line 135
What seems to be happening is that when $targetField->getType() on line 135 in src/Execution/Processor.php is called its returning the ArticleField class, not the ArticleType class.
I would expect that to return the class as declared in the 'getType' method on the ArticleField class.
Am i going about this wrong for nesting fields? Or is there a bug in the library?
To accomplish this you only pass the Field class as the first argument.
class ArticleSummaryType extends AbstractObjectType
{
public function build($config)
{
$config
->addField('title', new StringType());
->addField('body', new StringType());
->addField(new ArticleField());
}
}
Then in the field class you can override getName to set the name for the field as needed or it will use the class name as the field name.
I want to substitute object to return sequence of different objects.
For example:
var http = Substitute.For<IHttp>();
http.GetResponse(Arg.Any<string>()).Returns(resourceString, resourceString2);
http.GetResponse(Arg.Any<string>()).Returns(x => { throw new Exception(); });
will return resourceString then resourceString2 then exception.
Or something like this:
var http = Substitute.For<IHttp>();
http.GetResponse(Arg.Any<string>()).Returns(resourceString, x => { throw new Exception(); }, resourceString2);
will return resourceString then exception then resourceString2.
How can I do that?
This is now a supported feature in NSubstitute with a very friendly interface.
It would be something like...
var http = Substitute.For<IHttp>();
http.GetResponse(Arg.Any<string>()).Returns(
x => resourceString,
x => resourceString2,
x => { throw new Exception(); }
);
Documentation can be found here
This answer is outdated — NSubstitute has direct support for this now. Please see #dangerdex's answer to this question for more information.
The multiple returns syntax in NSubstitute only supports values. To also throw exceptions you'll need to pass a function to Returns, and implement the required logic yourself (e.g. Returns(x => NextValue())).
There is a related example for Moq sequences on Haacked's blog using a queue. You can do a similar thing with NSubstitute (example code only, use at your own risk :)):
public interface IFoo { int Bar(); }
[Test]
public void Example() {
var results = new Results<int>(1)
.Then(2)
.Then(3)
.Then(() => { throw new Exception("oops"); });
var sub = Substitute.For<IFoo>();
sub.Bar().Returns(x => results.Next());
Assert.AreEqual(1, sub.Bar());
Assert.AreEqual(2, sub.Bar());
Assert.AreEqual(3, sub.Bar());
Assert.Throws<Exception>(() => sub.Bar());
}
public class Results<T> {
private readonly Queue<Func<T>> values = new Queue<Func<T>>();
public Results(T result) { values.Enqueue(() => result); }
public Results<T> Then(T value) { return Then(() => value); }
public Results<T> Then(Func<T> value) {
values.Enqueue(value);
return this;
}
public T Next() { return values.Dequeue()(); }
}
Hope this helps.
Here's an example that does everything inline without an extra class. If you were doing this a lot I would probably go with the separate class option.
[Test]
public void WhenSomethingHappens()
{
var something = Substitute.For<ISomething>();
int callCount = 0;
something.SomeCall().Returns(1, 2);
something.When(x => x.SomeCall()).Do(obj => { if (++callCount == 3) throw new Exception("Problem!"); });
Assert.AreEqual(1, something.SomeCall());
Assert.AreEqual(2, something.SomeCall());
Assert.Throws<Exception>(() => something.SomeCall());
}
public interface ISomething
{
int SomeCall();
}
Another example with out parameter.
Moreover it is usueful for some scenarios when function is called periodically in separated thread. We can simulate/mock each call differently.
List<AnyClass> items = new List<AnyClass>();
mockIService.TryDoSth(out response).ReturnsForAnyArgs(p =>
{
p[0] = items;
return true;
},
p =>
{
p[0] = items;
return true;
}, p =>
{
throw new Exception("Problem!");
});
bool TryDoSth(out List result);
I am working on a project which includes a REST API component. I have a controller dedicated to handling all of the REST API calls.
Is there any way to catch all exceptions for that specific controller so that I can take a different action for those exceptions than the rest of the application's controllers?
IE: I'd like to respond with either an XML/JSON formatted API response that contains the exception message, rather than the default system view/stack trace (which isn't really useful in an API context). Would prefer not having to wrap every method call in the controller in its own try/catch.
Thanks for any advice in advance.
You can completely bypass Yii's default error displaying mechanism by registering onError and onException event listeners.
Example:
class ApiController extends CController
{
public function init()
{
parent::init();
Yii::app()->attachEventHandler('onError',array($this,'handleError'));
Yii::app()->attachEventHandler('onException',array($this,'handleError'));
}
public function handleError(CEvent $event)
{
if ($event instanceof CExceptionEvent)
{
// handle exception
// ...
}
elseif($event instanceof CErrorEvent)
{
// handle error
// ...
}
$event->handled = TRUE;
}
// ...
}
I wasn't able to attach events in controller, and I did it by redefinition CWebApplication class:
class WebApplication extends CWebApplication
{
protected function init()
{
parent::init();
Yii::app()->attachEventHandler('onError',array($this, 'handleApiError'));
Yii::app()->attachEventHandler('onException',array($this, 'handleApiError'));
}
/**
* Error handler
* #param CEvent $event
*/
public function handleApiError(CEvent $event)
{
$statusCode = 500;
if($event instanceof CExceptionEvent)
{
$statusCode = $event->exception->statusCode;
$body = array(
'code' => $event->exception->getCode(),
'message' => $event->exception->getMessage(),
'file' => YII_DEBUG ? $event->exception->getFile() : '*',
'line' => YII_DEBUG ? $event->exception->getLine() : '*'
);
}
else
{
$body = array(
'code' => $event->code,
'message' => $event->message,
'file' => YII_DEBUG ? $event->file : '*',
'line' => YII_DEBUG ? $event->line : '*'
);
}
$event->handled = true;
ApiHelper::instance()->sendResponse($statusCode, $body);
}
}
In index.php:
require_once(dirname(__FILE__) . '/protected/components/WebApplication.php');
Yii::createApplication('WebApplication', $config)->run();
You can write your own actionError() function per controller. There are several ways of doing that described here
I'm using the following Base controller for an API, it's not stateless API, mind you, but it can serve just aswell.
class BaseJSONController extends CController{
public $data = array();
public $layout;
public function filters()
{
return array('mainLoop');
}
/**
* it all starts here
* #param unknown_type $filterChain
*/
public function filterMainLoop($filterChain){
$this->data['Success'] = true;
$this->data['ReturnMessage'] = "";
$this->data['ReturnCode'] = 0;
try{
$filterChain->run();
}catch (Exception $e){
$this->data['Success'] = false;
$this->data['ReturnMessage'] = $e->getMessage();
$this->data['ReturnCode'] = $e->getCode();
}
echo json_encode($this->data);
}
}
You could also catch dbException and email those, as they're somewhat critical and can show underlying problem in the code/db design.
Add this to your controller:
Yii::app()->setComponents(array(
'errorHandler'=>array(
'errorAction'=>'error/error'
)
));
I have a really simple class with two methods; One that will be called and the other that it will call. The idea is to call the OuterMockMethod method BUT mock the InnerMockMethod. Right now I can only seem to mock the OuterMockMethod method.
public class MockClass : IMockInterface
{
public virtual MockClass InnerMockMethod()
{
MockClass returnValue;
returnValue = new MockClass();
returnValue.SomeMessage = "Not mocked";
return returnValue;
}
public virtual MockClass OuterMockMethod()
{
MockClass mock;
mock = new MockClass();
return mock.MockedMethod();
}
}
Now this works, but it isn't the method I want to mock:
public void MockTest_Internal()
{
MockClass returnedClass;
MockClass mockProvider;
mockProvider = repository.StrictMock<MockClass>();
mockProvider.Expect(item => item.OuterMockMethod())
.Return(new MockClass { SomeMessage = "Mocked" });
repository.Replay(mockProvider);
returnedClass = mockProvider.OuterMockMethod();
Assert.IsTrue(returnedClass.SomeMessage == "Mocked");
}
As you can see, it calls the OuterMockMethod which it likes but I don't want that. I want to mock the InnerMockMethod so that when it's called by the OuterMockMethod it will return what I want it to.
public void MockTest_Internal()
{
MockClass returnedClass;
MockClass mockProvider;
mockProvider = repository.StrictMock<MockClass>();
mockProvider.Expect(item => item.InnerMockMethod())
.Return(new MockClass { SomeMessage = "Mocked" });
repository.Replay(mockProvider);
returnedClass = mockProvider.OuterMockMethod(); //Boom angry Rhino
Assert.IsTrue(returnedClass.SomeMessage == "Mocked");
}
In this case you need to put the mock on the returned object:
MockClass returnedMock = MockRepository.GenerateMock<MockClass>();
returnedMock.Expect( rm => rm.InnerMockMethod() )
.Return( new MockClass { SomeMessage = "Mocked" } );
mockProvider.Expect( mp => mp.OuterMockMethod() ).Return (returnedMock );
returnedClass = mockProvider.OuterMockMethod();
...
Note that StrictMock has been deprecated. The preferred pattern is now AAA (Arrange, Act, Assert). You can find more info here.