Is using test factories in Laravel an anti pattern? - laravel

On this slide :
Sandiz Metz talks about how we should test from the outside without knowing anything about what goes inside the SUT.
I think her talk is focused on the unit tests, not so much about integration tests, but that still made me wonder...could using the factories in Laravel be an anti-pattern ?
It seems to me that using these factories mean I know how the database data has to be for the SUT to complete its task.
For example, say a user has to be able to edit his profile. With factories, I could do this :
/** #test */
public function the_user_can_update_his_profile()
{
$user = factory(User::class)->create();
// ACT
// ASSERT
}
But that knowledge seems to be far too deep and detailed. I have to know how to create a properly registered user. Following the idea of staying in the outside world, shouldn't I instead use an object that already exist to prepare the data for my tests ?
/** #test */
public function the_user_can_update_his_profile()
{
$userRepository = app(UserRepository::class);
$user = $userRepository->register('email#email.com', 'password123');
// ACT
// ASSERT
}
Going even further, how do I know this is the right way to do it ? Shouldn't I simply call the route that a user will use to register ?
/** #test */
public function the_user_can_update_his_profile()
{
$response = $this->json('POST', route('register_user'), ['email' => 'email#email.com', 'password' => 'password123']);
$userRepository = app(UserRepository::class);
$user = $userRepository->find($response['userId']);
// ACT
// ASSERT
}
But that (extreme) solution could also do tons of unnecessary other things (e.g. sending a confirmation email). It also needs the register route to work.
What did you experience as being the cleanest solution for a complicated project ?

As stated in the comments, you're mistaking the purpose and scope of a unit test and an integration test, and from this comes the confusion. Let's start with your "test":
/** #test */
public function the_user_can_update_his_profile()
{
$response = $this->json('POST', route('register_user'), ['email' => 'email#email.com', 'password' => 'password123']);
$userRepository = app(UserRepository::class);
$user = $userRepository->find($response['userId']);
// ACT
// ASSERT
}
This is an integration test. You are testing a "remote" route (okay, it's internal, but you're still testing every single component along the way). An integration test is perfect to confirm that business logic was implemented properly and that the route behaves as expected; it goes through its path from start to completion, testing every interaction between components and the logic in the controller+view itself.
This is all fine and good, but it doesn't really help us that much. We're still blind regarding testability of components themselves. This is where unit testing comes in.
Assume you have a class as follows:
<?php
class Foo {
public $value = 0;
public function __construct($value) {
$this->value = (int)$value;
}
public function getRemainder(int $item) {
return $this->value % $item;
}
}
You may very well end up using this class in one of your controllers; the point of unit testing this is to assert that:
the constructor does store the right value (as an integer)
the isModulo method does what it says on the tin.
To do so, we might write the following test:
public function isActualModuloClass() {
$modulo = new Foo(5);
$modulo_float = new Foo(2.3);
assert($modulo->value == 5, "Integer modulo constructor works");
assert($modulo_float->value == 2, "Float modulo casts to integer properly");
assert($modulo->getRemainder(5) == 0, "Modulo 5%5 is 0");
assert($modulo->getRemainder(4) == 1, "Modulo 5%4 is 1");
}
And sure enough, we've tested every single method, every single branch of our component in isolation. This is a unit test.
It gets messier with classes that interact with other objects, but when structured properly, it is very easy to leverage tools to inject mock copies of objects in order to be able to stub out interactions. When your code touches multiple things at once in an unclean fashion, your tests tend to end up a being gargantuan mess.
The talk you linked is about these tests, and ways to make them not be the fragile mess they tend to be in badly thought-out codebases. In theory, the author is right as well - you start from objects without dependencies, test those, then gradually go up assuming the contracts being established by those are sane and tested, and you work your way up the pyramid. In practice, it's very hard to do when the code is written with testing as an afterthought.

Related

Can I change this feature test to a unit test?

I have written two tests: one that makes a post request to an endpoint and awaits for a specific response containing status and message; and another one making the exact same request, but instead of await a response, it verifies if the database has the data matching what I just sent. Both these test are feature tests, and so far I have no unit test in my application; that happens because I have tested endpoint only.
So my idea is the following: instead of making a call to an endpoint in my second test, I could directly test my service method that creates a new register to the database. Would this be a valid unit test?
Personally, I think it would be valid because I am isolating a specific method and testing if the code works, and not if the integration works, even though there's integration of my code with the DB (Eloquent), my service method is the closest testable thing to the DB I have in my system.
My two tests, in the order I specified above:
/** #test */
public function a_group_can_be_created()
{
$this->withoutExceptionHandling()->signIn();
$group_data = [
'name' => $this->faker->word(),
'status' => $this->faker->boolean(),
];
$modules = ['modules' => Modules::factory(1)->create()->pluck('id')];
$response = $this->post(route('cms.groups.store'), array_merge($group_data, $modules));
$response->assertSessionHas('response', cms_response(trans('cms.groups.success_create')));
}
/** #test */
public function creating_a_group_persists_its_data_to_the_database()
{
$this->withoutExceptionHandling()->signIn();
$group_data = [
'name' => $this->faker->word(),
'status' => $this->faker->boolean(),
];
$modules = ['modules' => Modules::factory(1)->create()->pluck('id')];
$this->post(route('cms.groups.store'), array_merge($group_data, $modules));
$this->assertDatabaseHas('groups', $group_data);
$this->assertDatabaseCount('modules', 2);
$this->assertDatabaseCount('group_modules', 2);
}
Unit test in laravel "do not boot your Laravel application and therefore are unable to access your application's database or other framework services"
With that said, you can't access any database with a facade or with your eloquent model
so if you change your feature testing to unit testing, it will fail.
Unit test will work well if you don't use any of laravel framework utilities. I occasionally use it for testing a little self made library
But if you want to isolate it and create feature testing without calling the API endpoint, it will works too. It's really up to you to decide whether it is necessary to do that or not. But keep in mind that unnecessary test will make the test longer, especially if you use RefreshDatabase or DatabaseMigration trait. It will quite annoying to wait for them to finish

Resolving service based on model in AppServiceProvider

To give you a brief view: I am trying to make redirect based on conditions. If request satisfy conditions of given destination you will get redirect to certain destination.
I have Destination model which has many conditions (Condition model). I have many Conditions which extends basic Condition model like DateCondition, LocationCondition etc (made by polimorphic relation). Each of condition type should has 'service' which tells if given condition match do the request. Example DateCondition should has own DateConditionMatcher which implements ConditionMatcherInterface.
(just public function match($condition, $request)).
I would like to write it with Open Closed principle from SOLID. Firstly I thought to add getMatcher() function straight to condition model and return different ConditionMatcher for each condition type, but some of ConditionMatchers need some other services passed in contructor, so it would force me to inject them also in Condition models which is bad pratice.
Maybe using Contextual binding in ServiceProvider could resolve this but how?
I have no idea how to couple Model to to the right one ConditionMatcher to then use it freely like this:
foreach ($destination->conditions as $condition){
$isMatched = $this->conditionMatcher->match($condition, $request);
}
To always have correct ConditionMatcher under $this->conditionMatcher.
I hope someone understood my not very clear message.
I had an idea overnight to make service which keeps all the matchers. The only downside to this solution is injecting every matcher.
class ConditionResolver implements ConditionResolverInterface
{
/** #var ConditionMatcherInterface[] */
private $conditionMatchers = [];
public function __construct(
DateConditionMatcher $dateConditionMatcher,
TimeConditionMatcher $timeConditionMatcher,
LocationConditionMatcher $locationConditionMatcher
) {
$this->conditionMatchers[DateCondition::class] = $dateConditionMatcher;
$this->conditionMatchers[TimeCondition::class] = $timeConditionMatcher;
$this->conditionMatchers[LocationCondition::class] = $locationConditionMatcher;
}
}
so now I am able to use correct matcher to given condition in this way(simplified):
foreach ($model->conditions as $condition)
{
$this->conditionMatchers[get_class($condition)]->match($condition, $request);
}
It allows me to inject various of services into that matchers in AppServiceProvider.
$this->app->singleton(LocationServiceInterface::class, function($app){
return new LocationService();
});
$this->app->singleton(DateConditionMatcher::class, function($app){
return new DateConditionMatcher();
});
$this->app->singleton(TimeConditionMatcher::class, function($app){
return new TimeConditionMatcher();
});
$this->app->singleton(LocationConditionMatcher::class, function($app){
return new LocationConditionMatcher($app->make(LocationServiceInterface::class));
});
Overall I think I miessed something and it would be done in more elegant way, but for now I treat it as an answer. If you have better solution, please share :)

How to assert $next in Laravel Middleware

Lavarvel application running 7.10
I have a middleware that gets called on several routes. It check the user has the right relationships in place to get to the desired destination (that's not important).
I want a test that simply asserts that the user gets to the $next($request) in the middleware. I could of course just hit one of the end points, and assert something about that which would in turn validate the middleware, but all the routes hit external apis to get some other data which I could mock in my tests, which all seem pretty ugly just to test a middleware class.
I my test I can factory up the relevant relations etc, so whenever the middleware is called it should allow me straight in.
If I do this...
/** #test */
public function middleware_successful_test()
{
$user = factory(User::class)->create();
// some other factoried user relations here
$this->actingAs($user);
$request = new Request;
$middleware = app(MiddlewareClass::class);
$result = $middleware->handle($request, function ($req) {});
// don't know what assert here. $result is basically empty
}
Any tips, advise welcome.
Normally i would not unit test middlewares, but test it as a feature.
For your case i would utilize that you provide the next closure and you get the result. Return true and assert it is what is returned and therefor the closure has been called.
$result = $middleware->handle($request, function ($req) { return true;});
$this->assertSame(true, $result);

Different state for Eloquent model fields depending on current user in laravel

I have the model:
class Task extends Model {
}
with some fields
protected $fillable = ['message', 'due_time', 'status', 'etc...'];
I've added custom function:
public function getEditableStateFor{AttributeName}
In my helper function I check that if
method_exists($class, 'getEditableStateForField1')
than I allow to edit this field depending on boolean value returned from this function.
Example:
if( ! $class->getEditableStateForField1() ) {
return "You can not edit field field1";
}
Here is how looks like some functions in Task:
private function isCreator() {
$user = Auth::user();
if($user) {
return $user->id === $this->creator_id;
}
return false;
}
public function getEditableStateForMessage() {
return $this->isCreator();
}
public function getEditableStateForDueTime() {
return $this->isCreator();
}
Is this a good way to do it or it is very bad design because of hidden dependency on Auth::user()?
What is a better way?
I do not want to put this logic inside controllers because this logic propagates to another models and is universal across application.
I'm like you and like to have Models that contain as much of the business logic as possible while remaining totally free of depencies on the "web" part of the application, which I believe should stay in Controllers, Request objects, etc. Ideally, Models should be easily usable from command line interfaces to the application, from within the Tinker REPL, and elsewhere while still guaranteeing data integrity and that business rules are observed.
That said, it seems the Laravel creators had slightly different ideas, hence the Auth facade being easily available in the model.
What I would likely do is add a parameter of type User to the getEditableStateFor series functions, and then in turn pass that parameter to isCreator ($user) and elsewhere. That also frees you up to be able to allow associated users to edit each other's Tasks if that ever became a desired feature in the future.
Edit: another, perhaps better or perhaps worse, is to have an instance method like setCurrentUser ($user) then use setFieldNameAttribute methods so that the controller doesn't have to check the editability of fields, keeping that the model's responsibility. Then you could call the getEditableStateFor methods, which now check for the current user set by the above method (maybe falling back to Auth::user() or throwing a helpful error), inside the setter.

Where to place business logic in Symfony2?

After reading a lot of posts and Stack Overflow resources, I've still got some problems about the famous question about "where to put business logic?" Reading StackOverflow Question and A Blog Post, I believe I've understood the issue of code separation well.
Suppose I have a web form where you can add a user that will be added to a db. This example involves these concepts:
Form
Controller
Entity
Service
Repository
If I didn't miss something, you have to create an entity with some properties, getters, setters and so on in order to make it persist into a db. If you want to fetch or write that entity, you'll use entityManager and, for "non-canonical" query, entityRepository (that is where you can fit your "query language" query).
Now you have to define a service (that is a PHP class with a "lazy" instance) for all business logic; this is the place to put "heavy" code. Once you've recorded the service into your application, you can use it almost everywhere and that involves code reuse and so on.
When you render and post a form, you bind it with your entity (and with constraints of course) and use all the concepts defined above to put all together.
So, "old-me" would write a controller's action in this way:
public function indexAction(Request $request)
{
$modified = False;
if($request->getMethod() == 'POST'){ // submit, so have to modify data
$em = $this->getDoctrine()->getEntityManager();
$parameters = $request->request->get('User'); //form retriving
$id = $parameters['id'];
$user = $em->getRepository('SestanteUserBundle:User')->find($id);
$form = $this->createForm(new UserType(), $user);
$form->bindRequest($request);
$em->flush();
$modified = True;
}
$users = $this->getDoctrine()->getEntityManager()->getRepository('SestanteUserBundle:User')->findAll();
return $this->render('SestanteUserBundle:Default:index.html.twig',array('users'=>$users));
}
"New-me" has refactored code in this way:
public function indexAction(Request $request)
{
$um = $this->get('user_manager');
$modified = False;
if($request->getMethod() == 'POST'){ // submit, so have to modify data
$user = $um->getUserById($request,False);
$form = $this->createForm(new UserType(), $user);
$form->bindRequest($request);
$um->flushAll();
$modified = True;
}
$users = $um->showAllUser();
return $this->render('SestanteUserBundle:Default:index.html.twig',array('users'=>$users));
}
Where $um is a custom service where all code that you can't see from #1 code piece to #2 code piece is stored.
So, here are my questions:
Did I, finally, get the essence of symfony2 and {M}VC in general?
Is the refactor a good one? If not, what would be a better way?
Post Scriptum: I know that I can use the FOSUserBundle for User store and authentication, but this is a basic example for teach myself how to work with Symfony.
Moreover, my service was injected with ORM.Doctrine.* in order to work (just a note for who read this question with my same confusion)
There are two main approaches regarding on where to put the business logic: the SOA architecture and the domain-driven architecture. If your business objects (entities) are anemic, I mean, if they don’t have business logic, just getters and setters, then you will prefer SOA. However, if you build the business logic inside your business objects, then you will prefer the other. Adam Bien discusses these approaches:
Domain-driven design with Java EE 6: http://www.javaworld.com/javaworld/jw-05-2009/jw-05-domain-driven-design.html
Lean service architectures with Java EE 6: http://www.javaworld.com/javaworld/jw-04-2009/jw-04-lean-soa-with-javaee6.html
It’s Java, but you can get the idea.
Is the refactor a good one? If not, what would be a better way?
One of the best framework practices is using param converters to directly invoke an entity from user request.
Example from Symfony documentation:
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
/**
* #Route("/blog/{id}")
* #ParamConverter("post", class="SensioBlogBundle:Post")
*/
public function showAction(Post $post)
{
}
More on param converters:
http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html
Robert C. Martin (the clean code guy) says in his new book clean architecture, that you should put your business logic independently from your framerwork, because the framework will change with time.
So you can put your business logic in a seperate folder like App/Core or App/Manager and avoid inheretence from symfony classes here:
<?php
namespace App\Core;
class UserManager extends BaseManager implements ManagerInterface
{
}
I realize this is an old question, but since I had a similar problem I wanted to share my experience, hoping that it might be of help for somebody.
My suggestion would be to introduce a command bus and start using the command pattern. The workflow is pretty much like this:
Controller receives request and translates it to a command (a form might be used to do that, and you might need some DTO to move data cleanly from one layer to the other)
Controller sends that command to the command bus
The command bus looks up a handler and handles the command
The controller can then generate the response based on what it needs.
Some code based on your example:
public function indexAction(Request $request)
{
$command = new CreateUser();
$form = $this->createForm(new CreateUserFormType(), $command);
$form->submit($request);
if ($form->isValid()) {
$this->get('command_bus')->handle();
}
return $this->render(
'SestanteUserBundle:Default:index.html.twig',
['users' => $this->get('user_manager')->showAllUser()]
);
}
Then your command handler (which is really part of the service layer) would be responsible of creating the user. This has several advantages:
Your controllers are much less likely to become bloated, because they have little to no logic
Your business logic is separated from the application (HTTP) logic
Your code becomes more testable
You can reuse the same command handler but with data coming from a different port (e.g. CLI)
There are also a couple downsides:
the number of classes you need in order to apply this pattern is higher and it usually scales linearly with the number of features your application exposes
there are more moving pieces and it's a bit harder to reason about, so the learning curve for a team might be a little steeper.
A couple command buses worth noting:
https://github.com/thephpleague/tactician
https://github.com/SimpleBus/MessageBus

Resources