functional testing a service - php

I am having trouble testing some simple services that I have in my application. This is my service method:
public function createNewCourse($details = array())
{
$course = new Course($details);
$this->persistenceManager->save($course);
}
Basically I am sending an array to this service , create the $course object which sets the properties of the object. After that I call the persistenceManager which basically has a save method which inserts the object in the database. Anyway any tips of how to test this method without actually testing the persistence, because that will be another test.

If persistanceManager is injected to your object as a dependancy then you can create a mock object to represent it in your tests.
If it is not, then you will have great difficulty unit testing and you should refactor your code to use dependancy injection.
Don't worry, dependancy injection is just a fancy phrase for a pretty simple concept.
You are going to have similar problems unit testing the Course object.

Use a stub persistenceManager and provide it to the class that contains the createNewCourse method. The stub should look like this:
class StubPersistenceManager
{
private $course;
public function save($course)
{
$this->course = $course;
}
public function getCourse()
{
return $this->course;
}
}
Ensure you inject an instance of this stub as the $this->persistenceManager in your class that contains the createNewCourse method.
In the test you call the createNewCourse method and can subsequently fetch the course instance provided to the persistence manager by using the getCourse function of the stub.

Related

How to test create model service?

I decided to create CreateClassroomService to separte logic in my controller method.
class CreateClassroomService extends Service
{
public function create(string $name, User $user): ?Classroom
{
$this->checkName($name);
$classroom = new Classroom();
$created = $classroom->setName($name)
->associateUser($user)
->save();
return $created ? $classroom : null;
}
private function checkName(string $name): void
{
if (empty($name)) {
throw new InvalidArgumentException();
}
}
}
I am trying to test this service as part of learning unit testing, but I don't know how. I don't know how to mock the classroom object to control what the method should return. Does this mean that creating this service was not a good idea because I am not able to test it? Should I build this service differently? Unless the service can be tested, but I don't know how... What should I check in assertion?
This is my test but it is not good because I am not able to force what should be returned.
public function testGivenCreateCorrectDataClassroomWillBeCreated(): void
{
$name = 'Test classroom';
$user = Mockery::mock(User::class);
$result = $this->service->create($name, $user);
$this->assertTrue($result);
}
For something like this, you could simply assert that the ClassRoom has in fact been created. Docs
As per the docs, update your test class so that it's using the RefreshDatabase trait e.g.:
use Illuminate\Foundation\Testing\RefreshDatabase;
class ExampleTest extends TestCase
{
use RefreshDatabase;
Laravel has Model Factories to make creating models with dummy data very quick and easy. There should already be a UserFactory created for you (you may need to update it if you've updated the default users migration).
public function testGivenCreateCorrectDataClassroomWillBeCreated(): void
{
$name = 'Test classroom';
$user = User::factory()->create();
$result = $this->service->create($name, $user);
$this->assertInstanceOf(ClassRoom::class, $result);
$this->assertDatabaseHas('class_rooms', ['name' => $name]);
}
Don't forget to import the User and ClassRoom models in to your test class.
Not sure how mocking works in Mockery, but lets review what you are doing: your create function that you are trying to test creates a new instance of a Classroom, and since it instantiates the object inline you have no control over it.
From the code you have written, I am assuming that you build a classroom object and return, never using the same creator instance again, so what you can do is add a constructor to CreateClassroomService through which you inject a Classroom object. If you are using the same create service to create multiple classroom objects at a time, you will also need to make sure that you somehow reset the classroom instance to its default state inbetween creation(s), or you inject a fresh & new classroom object before invoking create again. This wholly depends on what the classroom does though.
The classroom object then can be mocked through unit testing -- you inject the mock instance and youre good to go. You actually are already injecting User object, so you're already on the right lines!
class CreateClassroomService extends Service {
private ?Classroom $classroom;
public function __construct(Classroom $classroom) {
$this->classroom = $classroom;
}
...
}
Now you just need to mock your classroom object in your test, inject the mock into your service when instantiating the object, and off you go. :)
Btw I would also say you may want to consider do some more abstraction on your service in the form of interfaces or review your parent class to make it more unit testable in general. Generally speaking for effective unit testing you want to avoid static methods and new keywords; where you can absolutely not avoid it, one approach might be to wrap just that one line of code in an encapsulated method, so you can mock the method instead to return you mock data / mock instances instead (but generally if you have to do this it should be an obvious sign to alert you to having sub-par architecture).

How to mock an aliased class with a method that returns an instance of itself?

I've been successfully using Mockery with PHPUnit tests lately. Yet, there is a dependency in a project I'm currently working that uses static method calls to interact with an API. I'm struggling to test one particular use case and it feels like I'll find other like this during the development roadmap.
Using this class as an example:
namespace Name\Space;
class User
{
/**
* #return \Name\Space\User[]
*/
public static function list(): array
{
// ...
}
public static function create(array $attrs): User
{
// ...
}
}
In case I just want to assert a method returns a primitive type, such as an array:
Mockery::mock('alias:\Name\Space\User')
->shouldReceive('list')
->andReturn([]);
It works fine, primarily because I'm not testing the array contents.
However, I have to call the create method, which returns an instance of the class itself (User). If I do something like this:
$user = new \Name\Space\User();
Mockery::mock('alias:\Name\Space\User')
->shouldReceive('create')
->andReturn($user);
The alias, obviously, won't work because the class was already loaded through the autoloader (composer's, in this case).
Does anyone have a suggestion on how to workaround this?
What about creating User in a closure?
<?php
$user = Mockery::mock('overload:\Name\Space\User')
->shouldReceive('create')
->andReturnUsing(function() {
return new \Name\Space\User();
});
Mocking static stuff is always painful.
I would recommend creating a Proxy object that is calling the static API calls and just returns the API results and inject this object everywhere you need to call the API.
This way it is easy to test by simply mocking the proxy object.
The proxy object itself can then be tested in an end to end test outside of the pure unit test scope.
You can still do more invasive stuff like this
https://www.pagemachine.de/blog/mocking-static-method-calls/?cn-reloaded=1
But writing code that doesn't belong to your unit tests purely to make something testable doesn't feel right to me.

PHP Unit testing dispatched events

How does one test that events were dispatched during a function call?
public function updateUser() {
//Do some update stuff
$event = new UserUpdated($user);
$event->attach([
new SendEmailAddressChangeEmail($emailAddress),
new SendEmailAddressChangeEmail($oldEmailAddress),
]);
$event->dispatch();
}
Aside from setting up an email address and seeing if an email is sent, how can I check (using PHP Unit) that the dispatcher is actually dispatching these events? I am assuming that I need to create a mock of some sort, but I am uncertain how to create a mock for a completely unrelated bit of code.
UserUpdated Event code:
class UserUpdated extends BaseEvent
{
public $user;
public function __construct(User $user) {
$this->user = $user;
}
}
and the related SendEmailAddressChanged Handler code:
class SendEmailAddressChangeEmail implements Contracts\HandlerInterface
{
protected $emailAddress;
public function __construct($emailAddress) {
$this->emailAddress = $emailAddress;
}
public function handle($event) {
EmailUtils::sendEmailAddressChangeEmail($this->emailAddress, $event->user->userName, $event->user->userID);
}
}
The updateUser() method you've got does two things in one that especially does not work well with (unit) testing:
business logic
object creation
From your own sense of things I assume this is also what made you ask this question. Often code that is not straight forward to test also is a good canary for design issues, so it is normally best to tackle w/ it.
These two points (1. and 2.) are an over-simplification of what is borrowed from the "Two piles" outlined by Misko Hevery in far more detail in his Clean Code Talks:
For example in "The Clean Code Talks -- Inheritance, Polymorphism, & Testing" from Nov 2008 - https://youtu.be/4F72VULWFvc?t=1328 ("Two Piles" # 22:08)
One solution to make this code more test-able is the use of dependency injection. That is one factory (method) for the user-event and one factory (method) for the object updateUser() is a method of. That concrete type then can make use of the factory object it gets injected to obtain the even object.
In short: If that update-user object needs a user-updated-event object it needs to ask for it in it's constructor.
As you sometimes don't want to create that user-updated-event object beforehand, the alternative is inject an object that knows how to create that user-updated-event object, these kind of objects are called factories.
The test then can inject a factory that presents an event mock object with the expectation that it is dispatched.
A good dispatch library btw. does already provide ready-made mocks for tests but that is out of the scope of Phpunit.
If you don't know yet about the mock functionality of Phpunit, please checkout the product's documentation for it:
Phpunit 7.1 Docs ยป 9. Test Doubles

How to add mocked expectations to a class under test

The code to be tested
abstract class Parent
{
public function getSomething(){} //this has to be mocked
}
class Child extends Parent
{
public function methodWhichIsTested()
{
$something = $this->getSomething(); //call to parent method
}
}
The test
public function setUp()
{
$this->child = new Child;
}
public function testTheChildMethod()
{
$this->child->methodWhichIsTested();
}
How can I add mock expectations to the instantiated class Child?
I would like to do something like:
MockFramework->takeExistingClass('Child')->shouldRecieve('getSomething')->andReturn('whatever');
My problem is, that in the real case (not the example), the getSomething method returns a dependency, which I need to mock!
I am using Mockery but if you know how to do this with phpUnit mocks, go ahead! Maybe I'm making a basic thinking mistake, please give me a hand! Thanks.
After you clarfied in chat that the returned value of getSomething holds a dependency that is
a protected property of the abstract class, and it is injected into that abstract via another public method of the abstract
the solution is inject a mock of that dependency via that other method.
In general, you should never have the need to mock or stub behavior of the TestSubject. It is only when you are making lookups to the Global Scope or mix/hard code object creation into the TestSubject, that you might see the need for that, but these would be code smells and should be refactored instead. See Sebastian Bergmann's articles on untestable code:
Testing private methods
Testing code that uses singletons
Stubbing static methods
Stubbing hard-coded dependencies

Pass by reference in a callback when mocking in PHPUnit

I have an interface I want to mock, and mock the behaviour of one of it's methods.
So I have created a callback that mocks the behaviour very simply.
This test passes if I create a new object that is based on this interface, but I would like to mock the interface.
The mocked setUp method is being called fine, and calling getVar('testing') in my callback returns the value. However my assertion fails, because that value isn't available.
It seems that you can't do this in PHPUnit? Unless I am being stupid.
Brief explanation of the code flow; The code in "getVar" calls a method which calls the "setUp" on the added plugin. When it calls "setUp" it passes in "$this". It is $this I am expecting to be passed by reference and which works with a "real" object.
class DefaultRendererTest extends \PHPUnit_Framework_TestCase
{
public function testSetGetVar()
{
$theme = $this->getMock('ThemeInterface');
$plugin = $this->getMock('PluginInterface');
$plugin->expects($this->once())
->method('setUp')
->will($this->returnCallback(function($r){
$r->setVar('testing', "fooBar");
}));
$renderer = new DefaultRenderer($theme, null);
$renderer->addPlugin($plugin);
$this->assertEquals('fooBar',$renderer->getVar('testing'));
}
}
For info here is the interface, the DefaultRenderer implements a RendererInterface
interface PluginInterface
{
function setUp(RendererInterface $renderer);
}
OK, out of interest, I tracked down the issue. It seems that PHPUnit automatically clones the parameters before the actual invocation takes place. I don't see a real reason for this, but maybe there is one. Taking a look at Framework/MockObject/Invocation/Static.php, there is only a single way how you can avoid this (on basis of the built in mock code): Implement a private __clone() method in the DefaultRenderer.
I'd also suggest you ask on IRC or the PHPUnit mailinglist about this behaviour or the mock object library.

Categories