Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit Tests for Controller (Symfony)

How can I create unit tests for this controller? I know how to make functional, but no idea about unit tests...

class CatalogController extends Controller
{
    /**
     * @param Request $request
     * @return View
     */
    public function getAllAction(Request $request)
    {
        $name = $request->query->get('name');
        $result = $this->getDoctrine()->getRepository('AppBundle:Category')->findBy(array('name' => $name));
        if ($result === NULL) {
            return new View("Catalog not found", Response::HTTP_NOT_FOUND);
        }
        return new View($result,Response::HTTP_OK);
    }
    /**
     * @param $id
     * @return View|object
     */
    public function getAction($id)
    {
        $result = $this->getDoctrine()->getRepository('AppBundle:Category')->find($id);
        if (!$result instanceof Category) {
            return new View("ID: " . $id . " not found", Response::HTTP_NOT_FOUND);
        }
        return new View($result, Response::HTTP_OK);
    }
    /**
     * @param Request $request
     * @return View|Response
     */
    public function postAction(Request $request)
    {
        $serializer = $this->get('jms_serializer');
        $content = $request->getContent();
        $category = $serializer->deserialize($content,'AppBundle\Entity\Category','json');
        $errors = $this->get('validator')->validate($category);
        if (count($errors) > 0) {
            return new View("NAME LENGTH MUST BE >4",Response::HTTP_BAD_REQUEST);
        } else {
            $em = $this->getDoctrine()->getManager();
            $em->persist($category);
            $em->flush();
            return new View($category, Response::HTTP_OK);
        }}}

.....................................................................................................................................................................

like image 927
Dialkord Avatar asked Jul 28 '26 00:07

Dialkord


1 Answers

There is an old joke that starts with "How do you get down off an elephant?" and ends with "You don't, you get down off a duck". Still cracks me up.

The point being is that if you keep your controller actions slim then you might not need to unit test them at all. Of course the 100% code coverage enforcers will disagree with this.

But if you are determined to test them then you will need to do some serious refactoring in order to preserve your own sanity. Lets take a look at your getAllAction:

public function getAllAction(Request $request)
{
    $name = $request->query->get('name');

So you would need to mock a request object, then mock an bag object and then add a test to see if get was called with a parameter of name. Painful at best. However, Symfony can actually inject request parameters automatically so:

public function getAllAction(string $name)
{

is all you need. Less code. Easy to test. What's not to like?

    $result = $this->getDoctrine()->getRepository('AppBundle:Category')->findBy(array('name' => $name));

Now this could be a real problem. If you look at the getDoctrine code you will see that it needs a container which holds a doctrine entity manager registry class which in turn holds an entity manager which then holds the repository. Do you really want to mock all these objects and string them together? You will spend far more time debugging your tests then your actual code. As a bonus, your code won't actually work anymore in SF4 which has moved away from the service locator pattern.

Fortunately, it's easy enough to fix using action injection:

public function getAllAction(string $name, CategoryRepository $categoryRepository)

You will have to do a bit of research to see how to define your repository as a service but it is not hard and reasonably easy to test. And once again we get rid of a rather nasty line of code.

Now this is a bit interesting:

return new View("Catalog not found", Response::HTTP_NOT_FOUND);

For a unit test we of course have no interest in the testing the View class itself. Rather all we want to know is if the View was constructed using the proper arguments. No easy way to intercept new operations.

Instead we can define and inject a ViewFactory

class ViewFactory
    public create($data,$status)
        return new View($data,$status)

So now it is easy enough to mock the view factory and test for the create method. As a bonus, the controller code is not tied quite as much to the View class.

So if you really really feel the need to unit test these sorts of actions then roll up your sleeves and start refactoring. I might add that looking at the new autowire functionaly in Symfony would be a good idea as well.

like image 107
Cerad Avatar answered Aug 03 '26 12:08

Cerad



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!