DonCallisto
DonCallisto

Reputation: 29912

UnitTest and Database

I would like to make some unittest for my application. As is my first time managing with PHPUnit (and unittest more in general) I would like to have an advice.

First, let's say that I have this class

class LodgingManager
{
    private $session;
    private $entity_manager;

        public function __construct(Session $session, EntityManager $em)
        {
            $this->session = $session;
            $this->entity_manager = $em;
        }

        public function loadLodgingList()
        {
            //If I've already fetched lodgings from db, use the session one.
            //Everytime a lodging is added, session is refreshed
            $lodging_list = $this->session->get('lodging_list');
            if (!$lodging_list) {
                $lodging_repo = $this->entity_manager->getRepository('KoobiBookingEngineBundle:Lodging');
                $lodging_list = $lodging_repo->getAllForList();

                $this->session->set('lodging_list', $lodging_list);
            }

            return $lodging_list;
        } 
        [...]
    }

This is a simple and silly method but, starting from here to me seems quite useful as code isn't to much complex to follow. As everyone can see I use a custom DQL method that helps me to retrieve some entities and place them into session.

I've created a test database with some dummy data and I would to test them.

As far as i know Doctrine's EntityManager has a protected __construct() method. This means that you can't use a "real" EntityManager but you need to use a mock one. Same consideration could be done for Repository and so on.

So the code I've producted is the following

class LodgingManagerTest extends \PHPUnit_Framework_TestCase
{
    const LODGING_CLASS = 'KoobiBookingEngineBundle:Lodging';

    /** @var LodgingManager */
    protected $lodging_manager;
    /** @var \PHPUnit_Framework_MockObject_MockObject */
    protected $em;
    /** @var \PHPUnit_Framework_MockObject_MockObject */
    protected $repository;
    /** @var \PHPUnit_Framework_MockObject_MockObject */
    protected $session;

    public function setUp()
    {
        $lodging_array_collection = new ArrayCollection();
        $lodging = $this->getMock('Koobi\BookingEngineBundle\Entity\Lodging');
        $lodging_array_collection->add($lodging);

        $repository = $this->getMockBuilder('Koobi\BookingEngineBundle\Repository\LodgingRepository')
            ->disableOriginalconstructor()
            ->getMock();
        $repository->expects($this->once())
            ->method('getAllForList')
            ->will($this->returnValue($lodging_array_collection));
        $this->repository = $repository;

        $em = $this->getMockBuilder('Doctrine\ORM\EntityManager')
            ->disableOriginalconstructor()
            ->getMock();
        $em->expects($this->once())
            ->method('getRepository')
            ->with($this->equalTo(static::LODGING_CLASS))
            ->will($this->returnValue($repository));
        $this->em = $em;

        $this->session = new Session(new MockArraySessionStorage());

        $this->lodging_manager = $this->createLodgingManager($this->session, $this->em);
    }

    public function testLoadLodgingList()
    {
        $lodging_list = $this->lodging_manager->loadLodgingList();
        $this->assertCount(1, $lodging_list);

    }
}

Every object of test is a mocked one (except for Lodging that is a "real" one). But to me this test is pretty useless as I can't load real entities from database.

I perfectly know that this kind of test is a silly one, but I can easily imagine some other complex one that need real objects.

So, asking to expert users, what's your opinion about? How can I procede to make more relevant and appropriate tests?

Upvotes: 2

Views: 102

Answers (2)

Matteo
Matteo

Reputation: 39380

IMHO you should do:

  1. unit testing your services for check that the "business logic" are done well, the flow of the code is correct and so on...
  2. functional test some component (custom repository method or specific services) with database fixture you make (the side effect is that you must maintain them and you must build the relation and so on)
  3. functional test your controller only minimal check (HTTP response code) possibly don't check about the DOM element of the page.

Hope this help

Upvotes: 1

piotrek
piotrek

Reputation: 14520

you should refactor you code to separate 'simple, stupid' dao from everything else (session management and business logic). then divide your tests into 2 or 3 groups.

  1. unit tests: testing components in isolation from environment (also from database). in this tests you mock your database (entityManager, repositories, however it's called in your technology) and you check if those mocks are being called as expected

  2. database tests: you use your real entityManager, repositories and mysql/oracle/whatever to test if your sql/orm code is working correctly. you are testing only your database code without any business logic or session management

  3. end-to-end tests. you start whole application and simulate users clicking web interface

Upvotes: 2

Related Questions