PHPUnit and throwing exceptions using Reflection - php

I'm not necessarily look for answer involving specifically Reflection, any answer that works will do.
I have the following abstract class which is extended by another class:
abstract class A_Class{
protected function returnSomething(array $param = ['some_argument' => false])
{
if(!is_bool($param))
{
throw new Exception('some message goes here');
}
}
}
class B_Class extends A_Class{}
And I'm using PHPUnit 4.8.27 by Sebastian Bergmann and contributors..
I have the following test
/**
* #expectedException \Exception
*/
public function testException()
{
$method = new ReflectionMethod(B_Class::class, 'returnSomething');
$method->setAccessible(true);
$method->invokeArgs(new B_Class(), ['some_argument' => 'string']);
}
When I run my tests the following message appears:
Failed asserting that exception of type "\Exception" is thrown.
I've google'd around a bit, I can't really find and answer as to what I'm doing wrong. To be perfectly honest I'm not even sure that I'm doing anything wrong. The problem itself may not be with my code as much as it is with the Reflection class. I don't know much about it and all the documentation is kind of, uhm, lacking. It may not be able to throw the exception defined in the reflected class.
Any pointers in the right direction here would be greatly appreciated.
What I've tried so far:
Using ReflectionClass instead of ReflectionMethod:
/**
* #expectedException \Exception
*/
public function testGetExcerptException()
{
$method = new ReflectionClass(new B_Class()::class);
$methodToCall = $method->getMethod('returnSomething');
$methodToCall->setAccessible(true);
$methodToCall->invokeArgs(new B_Class(), ['some_argument' => 'string']);
}
Setting the visibility to public, which of course works, but that kind of defeats the purpose.
In case anybody comes across this question. Don't do what I did. Even the The guy that wrote PHPUnit says it's a bad idea. All tested methods should be public.

An alternative solution from using Reflection, since you are using PHPUnit, is to use MockObjects.
Mocks in PHPUnit allow you to mock any public and protected methods for your classes.

Related

PHPStan misses interface implementation when inferring return type

I recently started playing with PHPStan, and encountered a roadblock that I just cannot get past. Some visual aid might make this easier for me to explain. Class names are simplified to keep things easy to read:
We have two interfaces: Enumerable and LeadContract. Then, we have another interface (LeadRepository) that features a method with this signature:
public function list(): Enumerable<int, LeadContract>;
The problem comes from the implementation we did of LeadRepository. In this implementation, we have a list method that returns Collection<int, LeadModel>, PHP gives us no problem with this, but PHPStan does.
What we've noticed is that PHPStan currently seems to:
Expect Enumerable<int, LeadContract>
But gets Collection<int, LeadModel>
Now, Collection implements Enumerable, and LeadModel implements LeadContract. I am just having a tough time figuring out why PHPStan continues to complain about it, even though we are implementing our interfaces in all the correct classes.
We tried to use generics, but then PHPStan would complain about us not using them in a parameter. We used them in the return type, but that didn't seem to satisfy that rule.
Any help is much appreciated!!!
After some sleep and looking through some examples, I fixed the issue by making the LeadRepository contract generic, and then specifying the Lead implementation as the generic parameter at the implementation level.
Example:
/**
* #template T of LeadContract
*/
interface LeadRepository
{
/**
* #return Enumerable<int, T>
*/
public function findByPhoneNumber(string $phoneNumber): Enumerable;
}
/**
* #implements LeadRepositoryContract<Lead>
*/
class LeadRepository implements LeadRepositoryContract
{
{...}
public function findByPhoneNumber(string $phoneNumber): Enumerable
{
return Lead::where([
'phone_number' => $phoneNumber,
])->get();
}
}
I used TypeScript as a basis for how PHPStan worked, and assumed it would infer the generic parameter from the function signature. Now I'm just wondering if there is a better way to do this?
Thank you #Ondrej! I think I needed to see the problem reproduced differently to figure out the solution.

PHP: what's the reason some people "fake" abstract methods by exclusively throwing an exception in the body?

Recently I often see code like the following (this example is taken from Symfony):
protected function execute(InputInterface $input, OutputInterface $output)
{
throw new LogicException('You must override the execute() method in the concrete command class.');
}
What is the benefit here over just marking the method as abstract?
What's the benefit for the ...
... library author?
... library user?
I already found a similar question for Java (Faking abstract methods using exceptions in Java), but it wasn't very helpful as its answers are explicit guessings and opinions.
In this particular case, the comments explain:
/**
* Executes the current command.
*
* This method is not abstract because you can use this class
* as a concrete class. In this case, instead of defining the
* execute() method, you set the code to execute by passing
* a Closure to the setCode() method.
*
* #return int|null null or 0 if everything went fine, or an error code
*
* #throws LogicException When this abstract method is not implemented
*
* #see setCode()
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
throw new LogicException('You must override the execute() method in the concrete command class.');
}
You could argue with the overall design as it is perhaps a bit hacky but it works well in practice. Take a look at Command::run to see where the decision to use a closure or execute is made. Bit of a niche case to say the least.
I know quite a bit of this was discussed in the comments of the other answer but I thought it might help to summarize. I also did a quick search through the Symfony framework code looking to see where the closure approach was used. Did not find anything. The closure support goes all the way back to the original 2.0 release so it might have one of those "seemed a good idea at the time" bits of functionality.
A possible use case is optional methods. If you make it abstract then all child classes need to implement it:
abstract class Database
{
abstract public function export();
}
class MySQL extends Database
{
}
Fatal error: Class MySQL contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Database::export)
If you make a regular method then child classes only need to implement the method if they plan to support it.
abstract class Database
{
public function export(){
throw new \LogicException(__CLASS__ . ' driver does not support ' . __FUNCTION__);
}
}
class MySQL extends Database
{
}
... yet you get a nice error if you attempt to use it:
$s = new MySQL();
$s->export();
Fatal error: Uncaught exception 'LogicException': MySQL driver does not support export
only abstract classes can have abstract methods.
You can't create object from abstract classes.
All abstract methods must be implemented in non-abstract classes.
Constructed Example:
Imagine the following:
abstract class Repository {
public abstract function read();
public abstract function write($object);
public abstract function delete($object);
public function connection() {
//this is implemented for you by the framework so you don't have to do it every time
}
Now you want to implement a repository for logging user-actions (logged-in, logged-out, ...)
You don't want any of those entries to be deleted and that's when you don't want to implement the delete function. But you have to, because your UserActionRepository is not abstract, because you actually need instances of it. that's when you throw an exception.

PHPUnit Fail When Method Being Stubbed Does Not Originally Exist

Is there a way to make PHPUnit throw an exception if a method being stubbed does not originally exist in the class being stubbed?
Here's a crude example:
class User {
function getId() { ... }
}
class LoginTest extends PHPUnit_Framework_TestCase {
function testLogin() {
...
$userMock = $this->getMockBuilder('User')
->setMethods(['getID']) // getId is misspelled; error should occur
->getMock();
...
}
}
class Login {
function login($user) {
...
$id = $user->getID(); // tests will pass even though this is misspelled
...
}
}
#Schleis is right that you cannot do it in PHPUnit directly.
But as of PHPUnit 4.5 you can use Prophecy to create test doubles. Prophecy will not tolerate this behavior. You won't be able to mock a non-existing method with it.
No, you can't.
PHPUnit doesn't need to have the class available to mock it. And you can even set the mock to not use any autoloading. When this happens, PHPUnit creates a fake class on the fly. This fake class doesn't have any defined methods which would cause an exception to be thrown, failing your tests suite.
Your tests should fail because of issues with the code that is being tested. Issues with the mock are outside of the scope of your test. The issue in your example would be caught during your functional testing.
There really isn't an easy way to tell between an misspelled function and one that hasn't been implemented yet.

How to mock a method from the class you are testing with Prophecy?

I want to use Prophecy ("phpspec/prophecy-phpunit") for the first time to create unit tests for my classes. I want to test a function that calls another function in the same service, here's the code:
class UserManager
{
private $em;
private $passwordHelper;
public function __construct(\Doctrine\ORM\EntityManager $em, \MainBundle\Helper\PasswordHelper $passwordHelper)
{
$this->em = $em;
$this->passwordHelper = $passwordHelper;
}
public function getUserForLdapLogin($ldapUser)
{
$dbUser = $this
->em
->getRepository('MainBundle:User')
->findOneBy(array('username' => $ldapUser->getUsername()));
return (!$dbUser) ?
$this->createUserFromLdap($ldapUser) :
$this->updateUserFromLdap($ldapUser, $dbUser);
}
First problem I had was that I was using findOneByUsername and Prophecy, as far as my knowledge goes, does not allow you to: mock magic methods (_call for EntityRepository), mock methods that do not exist, mock the class you are testing. If these are true I'm in a bit of a pickle, meaning I cannot test this function without testing the other functions in the class.
So far, my test looks like this:
class UserManagerTest extends \Prophecy\PhpUnit\ProphecyTestCase
{
public function testGetUserForLdapLoginWithNoUser()
{
$ldapUser = new LdapUser();
$ldapUser->setUsername('username');
$em = $this->prophesize('Doctrine\ORM\EntityManager');
$passwordHelper = $this->prophesize('MainBundle\Helper\PasswordHelper');
$repository = $this->prophesize('Doctrine\ORM\EntityRepository');
$em->getRepository('MainBundle:User')->willReturn($repository);
$repository->findOneBy(array('username' => 'username'))->willReturn(null);
$em->getRepository('MainBundle:User')->shouldBeCalled();
$repository->findOneBy(array('username' => 'username'))->shouldBeCalled();
$service = $this->prophesize('MainBundle\Helper\UserManager')
->willBeConstructedWith(array($em->reveal(), $passwordHelper->reveal()));
$service->reveal();
$service->getUserForLdapLogin($ldapUser);
}
}
And of course, the tests fail because the promises on $em, and the repository are not fulfilled. If I instantiate the class I am testing, the tests fail because the function then calls createUserFromLdap() on the same class and that is not tested.
Any suggestions?
First problem :
Don't use magic, magic is evil. __call may lead to unpredictable behavior.
"the promises on $em, and the repository are not fulfilled" :
Don't make your code depend on Class but Interface.
Then mock the Interface instead of Class !
You should mock ObjectManager instead of EntityManager. (don't forget to change the type of your parameters)
And the last point :
Before reveal.
$service->createUserFromLdap()
->shouldBeCalled()
->willReturn(null);
What you're trying to achieve is a partial mock, which is not supported by Prophecy. More about it here https://github.com/phpspec/prophecy/issues/101 and https://github.com/phpspec/prophecy/issues/61.
TL;DR; Design your classes with single responsibility in mind, so you don't have to mock other functionality.
Regarding your problem of not being able to mock methods that do not exist, you could use
http://docs.mockery.io/en/latest/
in stead of prophecy. Mockery allows you to do just that. Strictly speaking, that does break some of the rules of good design, but on the other hand, sometimes it's just very useful. Anyways, mockery is very similar, as far as features go, and it's equally as intuitive and easy to use imo. However, they still haven't released stable version, so just be aware of that if you do decide to use it.
Here you can find a good comparison of two libraries
http://everzet.com/post/72910908762/conceptual-difference-between-mockery-and-prophecy

PHPUnit doesn't catch expected exceptions

I have a set of tests, and I want to test that my classes throw exceptions at the right time. In the example, my class uses the __get() magic method, so I need to test that an exception is thrown when an invalid property is retrieved:
function testExceptionThrownWhenGettingInvalidProperty() {
$obj = new MyClass();
$this->setExpectedException("Property qwerty does not exist");
$qwerty = $obj->qwerty;
}
The class throws an error as it should, but instead of just getting a pass, the exception isn't caught!
There was 1 error:
1) QueryTest::testExceptionThrownWhenGettingInvalidProperty
Exception: Property qwerty does not exist
I was using SimpleTest before, and $this->expectException(new Exception("Property qwerty does not exist")); worked just fine. I know there are other methods (#expectedException and try-catch), but this one should work and it looks a lot cleaner. Any ideas how I can make this work?
It's not looking for the text in the exception, it's looking for the name of the exception class... Docs
$this->setExpectedException('Exception');
It's quite handy when you're using SPL Exceptions, or custom exception classes...
Adding to ircmaxell's answer, there is actually a simpler way of doing this:
/**
* #expectedException MyExceptionClass
* #expectedExceptionMessage Array too small
*/
public function testSomething()
{
}
The #expectedException the class name of the exception to expect, and #expectedExceptionMessage is a substring of the exception message to expect (that's right, you don't have to have the entire message).
If you prefer to not use docblock annotations, both of these are actually available as methods on the test case.

Categories