phpspec - check if a method was called with a specific argument (object) - php

I am writing a PHPspec test where want to test if a method was called with the correct argument (in the case an object).
I am doing something like this (pseudo code), inside my spec:
function it_some_test(MyService $myservice) {
$object = new MyDomainObject();
$object->setPropertyX("some value")
$myservice->getSomething($object)->shouldBeCalled();
}
The object is just a DTO created inside my service. Its created exactly the same way in the original Service. Just the value of some properties change based on same logic. thats what I want to test.
The problem is my test is always failing because it says the $object is different from what it was excepting but they have exactly the same properties values.
How does PHPspec compares objects? If I pass Argument::any() instead it works but I need to test the object passed as a property with a specific value.

I found the issue. I was calling the method ->shouldBeCalled() before calling the real method under test. Change the order and it works now.

Related

Is there a way to chain php methods while also returning something?

I have a recursively defined function called getPropertyValue($object, $property) that returns false if $property doesn't exist at an arbitrary depth into $object, and the value corresponding to the first $property found inside $object otherwise. It is essentially a generalized version of PHP's built-in function property_exists()
I want to make this method chainable, and I know that to do this, I would simply return a reference to the class instance ($this) in the method, but I am already returning, as I mentioned above, the value corresponding to the first $property found inside $object.
How can I make this method chainable while still returning this value? I'm thinking a possibility is to return an object containing both $this and the value of that property/false, if it is found/not found, respectively. Is this a viable approach? What would be the best way to achieve chainability in this context?
Thanks very much.
This kind of API doesn't really work when you want to return data.
It can be a convenience for building complex objects where numerous methods can be executed with less boilerplate code. The methods tend to be named descriptively, giving a good idea of what being done.
An example is the 'query builder' pattern, building the SQL statement via chained methods.
$sql = $qb->select()->from('table')->where()->field('foo')->equals('bar');
Don't be fooled however, this is just saving you from using setters.
$qb->setSelect();
$qb->setFrom('table');
$qb->setField('foo');
$qb->setEquals('bar');
Obvious you are not creating a query builder, and there are different implementations of the same API, however the key is they all add data to the object rather than return it.
you can return multiple values by putting them into an array:
return array($value,$this); // so you return both.

Laravel 4 - Container class: share function & closure logic

I have a follow-up question to the one discussed here:
Laravel core method confusion
I am in the same situation as driechel (author of question above) has been before, currently getting used to Laravel 4 FW and examining the core. Although a precise answer has been given I still don't understand the logic and what is happening under the hood. So I would very much appreciate a further explanation.
I know this might be a duplicate but since I cannot post comments yet I'll give it a shot with a new question. Hope it' ok this way.
I have been looking at this from another angle starting at this article:
http://blog.joynag.net/2013/05/facades-in-laravel-4-and-static-methods-resolution/
When examining the call File:get() I finally end up at the Container class' share function which is called with this actual parameter share(function() { return new Filesystem; }.
What I just can't figure out is the use of $container. Especially at the second occurence within the closure:
$object = $closure($container);
Could you please clarify this again? Why is $container passed as a parameter here and what is actually contained in it? As far as I understand $closure at that point holds and executes function() { return new Filesystem; } which has no input parameter.
I am lost. Studied this and the PHP anonymous functions/closures now for two days straight and still can't figure it out. I neither understand the syntax of $closure($container) here nor the logic.
For reference, this is the share method # v4.0.5.
So, what's happening here. I'll explain it in a couple of steps.
Calling The Share Method
As you pointed out this method is called from service providers. So, the FilesystemServiceProvider calls this method which looks something like this:
$this->app['files'] = $this->app->share(function() { return new Filesystem; });
It's assigning the return value of this share method to a binding in the container. In a nutshell, that return value will be the new Filesystem instance that is returned in the closure.
So What Does Share Do?
The share method is just another way of defining a singleton in IoC container. All this can be a bit intimidating at first. Basically, Laravel itself is an IoC container. All the classes are bound as instances on the container. Sometimes these instances should be the same instance on every call.
If you take a look at the referencing method above on GitHub, you'll notice that inside the closure a static variable is defined. It then checks if that variable is null, and if it is it resolves the closure (this is the closure that returns our new Filesystem instance). Then it simply returns the variable.
Now, the next time you use File::get() it doesn't need to instantiate the Filesystem class again, because it's already been instantiated and stored in the static $object variable. So it simply returns the same object to you.
So! Really, you could replace the $this->app['files'] line with this, and it would still work.
$this->app->instance('files', new Filesystem);
99% of services actually use the share method though because working inside a closure allows objects to be instantiated with more complex dependencies.
Hope this helps.

How can I test this method call with PHPUnit?

Code to be tested:
// Add the activation provider argument to the factory definition
$factoryDefinition = $container->getDefinition('gremo_subscription_factory');
$factoryDefinition->addArgument(new Reference($providerId));
Test method should check the addArgument method, including $providerId argument. I'm just learining PHPUnit and right now I'm only able to call $this->anything():
$container->expects($this->at(3))
->method('getDefinition')
->with('gremo_subscription_factory')
->will($this->returnValue($factory));
$factory->expects($this->once())
->method('addArgument')
->with($this->anything());
$this->pass->process($container);
How can I check that argument type is Reference class, and (in turn) its argument is exactly the string $providerId?
This is pretty complicated, especially since the Reference class is not dependency injected and method call doesn't return anything. However, I think you can get around it using argument constraints. Here's how I would do that second clause:
$factory->expects($this->once())
->method('addArgument')
->with($this->logicalAnd(
$this->isInstanceOf('Reference'),
$this->attributeEqualTo('attribute', $providerId)
));
The second item in the logicalAnd() is basically just checking the Reference object that is created to see if $providerId gets assigned correctly (I'm not sure what happens to $providerId in the Reference constructor, but I'm assuming it gets saved to an instance variable or something).
This sort of thing, however, is moving into the territory of testing implementation details of the Reference class, so tests like this are not great for maintaining SRP. All of this would be better solved by refactoring your code. Generally speaking, if it's hard to test, it is probably not the test suite's fault. If you are able to, consider changing things on that end first, rather than writing overly-clever tests.

Mock frameworks returns class with different name and type

I'm trying to create a mock to satisfy a typehint with this code (Mockery):
return \Mockery::mock('\Contracts\Helpers\iFileSystemWrapper');
or this (PHPUnit):
return $this->getMock('\Contracts\Helpers\iFileSystemWrapper');
But the mock returned is called Mockery\Mock Object or Mock_iFileSystemWrapper_a5f91049. How am I supposed to type check this when it isn't an instance of what I need at all with either framework?
Why exactly is the mock framework trying to load the real class? If I wanted the real class I would include the real class.
This problem has slowed me down so many times when writing tests I'm about to just toss type hinting out the window and check class names instead, or simply use production objects as mocks are a pain to use.
I just experimented with an existing test of my own, and by changing the interface namespace name from one that exists to one that doesn't exist, I got exactly the same as what you describe (using phpunit). My mock object had the class name Mock_ViewInterface_c755461e. When I change it back to the correct interface name, it works fine.
Therefore I would say that either:
You are trying to use an interface name that doesn't exist (e.g. a typo or missing namespace component).
Your library code isn't being loaded for some reason, e.g. autoloading is not setup correctly in your unit test bootstrap.
You need use a special function to check base class.
Somthing like this:
$mock = $this->getMock('MyClass');
$this->assertInstanceOf('MyClass', $mock);

Check that mock's method is called without any parameters passed (in phpunit)

In phpunit we can specify the method was called with particular
->with($this->equalTo('foobar'))
or any
->with($this->anything())
parameter.
But is there a way to specify that the method has been called without parameters at all?
This is the test I expect to fail:
public function testZ()
{
$a = $this->getMock('q');
$a->expects($this->once())
->method('z')
->with(); // <--- what constraint to specify here?
$a->z(1);
}
UPD:
The question has theoretical nature, so I have no any real life example. Some case it could be useful I can think of right now is:
public function testMe($object)
{
$object->foo();
}
And let's assume that testMe should (by design and by requirements) always call the method without parameters (assuming foo() has default ones). Because any non-default parameter (more precise: any parameter != to default one, which we don't know yet and which probably could change independently) in this case causes fatal consequences.
While rdlowrey is correct that with() doesn't make provisions for checking for no arguments passed, the problem doesn't lie with PHPUnit but PHP itself.
First, if your method doesn't provide default values, the interpreter will raise a fatal error if you don't pass any parameters. This is expected and not entirely relevant to the question at hand, but it's important to state up front.
Second, if your method does provide default values, calling the method without arguments will cause PHP to alter the call before PHPUnit gets involved to pass the defaults instead. Here's a simple test that demonstrates PHP inserting itself before PHP can check the parameters. It's key to realize that the mock class that PHP creates has the same signature as the mocked class--including the defaults.
class MockTest extends PHPUnit_Framework_TestCase {
public function test() {
$mock = $this->getMock('Foo', array('bar'));
$mock->expects($this->once())
->method('bar')
->with() // does nothing, but it doesn't matter
->will($this->returnArgument(0));
self::assertEquals('foobar', $mock->bar()); // PHP inserts 1 and 2
// assertion fails because 1 != 'foobar'
}
}
class Foo {
public function bar($x = 1, $y = 2) {
return $x + $y;
}
}
This means you can verify that either nothing was passed or the default values were passed, but you cannot be more specific.
Can you get around this limitation? You can remove default values from arguments when overriding methods, so you should be able to create a subclass and mock it. Is it worth it? My initial gut reaction is that this is a huge code smell. Either your design or your tests are doing the Wrong Thing(tm).
If you can provide a real-world, concrete example where you actually need to do this kind of test, it's worth spending some time pondering a solution. Until then, I'm satisfied with the purely academic answer of "don't do that." :)
PHPUnit mock objects can only use the ->with() constraint method to verify that the count or respective values of passed parameters match those passed to the mocked method when invoked. You can't use it to require that no arguments are passed to the mock method.
The mock verification process specifically checks that the passed parameter count and the associated values are compatible with those passed to the with constraint. Also, as you've probably seen, specifying the with constraint without any values won't work either; if with receives no parameters it won't add any parameter constraints for verification.
You can see the actual PHPUnit_Framework_MockObject_Matcher_Parameters::verify method used to verify mock method parameters in the linked github source.
If validation of no passed arguments is a requirement you'll need to specify your own mock class to verify such a condition outside the PHPUnit mocking capabilities.

Categories