PHPUnit and Magento? Yes you CAN!

POSTED ON Feb 1, 2011 IN in Extensions, How To

I think a lot of developers tried to write own UnitTests for Magento. I tried as well, but the main problem was in isolation of database for tests. So I came up with idea in developing a test suite for Magento that will help me to create tests without terrible headache in testing of data saving and retrieving… Some time ago I worked in a company, that was using PHPUnit with fixtures in YAML format for it and of course I liked this idea. So I use something similar in our development as well, but with some improvements and adaptation for Magento.

Now the beta version of extension is available on MagentoConnect at this link:

Also you can get the latests version from our Git Repository:

Public Roadmap

Is placed on github. Please use it for submitting your issues.

Developer’s Manual

EcomDev_PHPUnit Manual

So let me outline some interesting features in this extension:

Test Cases

Creation of test cases in your module is very easy.
Create the following directory structure in your module:

  • Test
    • Model
    • Block
    • Helper

And add your module to test suite configuration:

<?xml version="1.0"?>
                 <YouNamespace_Module />

And now when you run this command in your Magento root:

phpunit UnitTests.php

The extension will check all the modules that are defined in suite node for tests availability. You tests files can be nested as you wish, but all them should be Test directory and separated in the following groups: Model, Block, Helper. For now there is no group for controller, because mostly blocks load models, so you should test them for proper data retrieving.

Also you can run unit tests by groups. The extension groups test cases by module name and type.
For instance:

phpunit --group models --group YouNamespace_Module UnitTests.php

will run only tests in “Model” group and defined in “YouNamespace_Module” module.

phpunit --group models UnitTests.php

will run only tests in “Model” group.


If you don’t know what is fixtures, please read this chapter in PHPUnit manual.

In Magento fixtures is mostly used for Database and configuration. So I implemented both of them.

Let look at this small example:

class EcomDev_Example_Test_Model_Order extends EcomDev_PHPUnit_Test_Case
     * Retrieves list of order ids for some purpose
     * @test
     * @loadFixture
    public function orderList()
        $collection = Mage::getModel('sales/order')->getCollection();
        // Check that number of items the same as expected value

If you are familiar with all PHPUnit anotations, you will notice a new one “@loadFixture”. It can have additional argument, that indicates the name of fixture. If it is empty, the name of the test will be used instead. In our case it will be orderList. You should store your fixtures in the following path (the same directory with your test case file):


So let’s create our fixture:

    - entity_id: 1
    - entity_id: 2
    - entity_id: 3

tables keyword indicates fixture type. For now there are only 2 available: tables and config.

Tables construction template looks like the following:

    - [column_name]: [column_value]
      [another_column_name]: [column_value]

Config looks simplier:

   [node_path]: [node_value]
   [another_node_path]: [node_value]

By the way you may use as many fixtures as you want, they will be merged into big one.

So what will happen when you run your tests step by step?

  1. On setUp all @loadFixture statements will be parsed and executed.
  2. You test routine
  3. On tearDown all the data in configuration will be reverted and modified tables will be cleared

Now try to run the test case and see it in action.

phpunit --filter EcomDev_Example_Test_Model_Order UnitTests.php


But what if you don’t want to hard-code expected results in your code? That’s why there is expectations were introduced (@loadExpectation). It is loaded in similar way as fixtures, so you can use as many as you want. But them are placed in expectations directory and there is no limitation in the structure of the file, it depends on your imagination.
You can access expectations via _getExpectation method inside of the test. It returns Varien_Object, so you can navigate easily to any number of nested levels via getData method.

So let add the expectations to previous test case and add some additional checks to it:

class EcomDev_Example_Test_Model_Order extends EcomDev_PHPUnit_Test_Case
     * Retrieves list of order ids for some purpose
     * @test
     * @loadExpectation
    public function orderList()
        $collection = Mage::getModel('sales/order')->getCollection();
        // Check that number of items the same as expected value
        // Check that the order ids is the same as expected ones

The fixture will be used the same, so we will leave it as is.

The expectations file:

count: 3
order_ids: [1, 2, 3]

Data Providers

But what if you have a lot of similar tests, that just uses different input data?
Then data providers will help you. PHPUnit gives ability to use data providers as class methods, but we want to perform less hard-code, so there was created default data provider that is called dataProvider. It loads yaml file with the same name as test from providers directory.

So let create a new test that will have different status for each data set:

class EcomDev_Example_Test_Model_Order extends EcomDev_PHPUnit_Test_Case
    // ....Here goes your previous test....
     * Filters order collection by state and checks expected data
     * @param string $state
     * @param string $expectation
     * @test
     * @loadFixture
     * @loadExpectation
     * @dataProvider dataProvider
    public function orderStatusFilter($state, $expectation)
        $collection = Mage::getModel('sales/order')->getCollection();
        $collection->addFieldToFilter('state', $state);
        // Check that number of items the same as expected value
            $this->_getExpectations()->getData($expectation . '/count'),
        // Check that the order ids is the same as expected ones
            $this->_getExpectations()->getData($expectation . '/order_ids'),

$expectation variable in the above code contains name of expectation data set.


    - entity_id: 1
      state: pending
      status: pending
    - entity_id: 2
      state: processing
      status: processing
    - entity_id: 3
      state: processing
      status: processing
    - entity_id: 4
      state: canceled
      status: canceled

Expectations file will have 3 datasets: first, second and third.

  count: 1
  order_ids: [1]
  count: 2
  order_ids: [2, 3]
  count: 1
  order_ids: [4]

And now we will create data provider for these data sets:

  - pending # orders with pending status will be retrieved
  - first
  - processing # orders with processing status will be retrieved
  - second
  - canceled # all canceled orders will be retrieved
  - third

You have found an issue?

Please post all your issues here It will help us to make it better.

Have Fun With Unit Test!

Further Reading

New Version Of EcomDev_PHPUnit 0.1.2 Extension For Magento

Ivan Chepurnyi
Magento guru / System architect
Ivan started as Magento Core developer in early 2007, since that time he already has 6 years of experience in different areas of Magento development. During all that time he developed enormous amount of modules and customizations, so now almost every dark corner in Magento functionality is investigated by him. He can’t keep that knowledge in secret, that why he's sharing it with the community and helps finding the way out of Magento complexity maze.

57 Responses to “PHPUnit and Magento? Yes you CAN!”

  1. Looks interesting. Thanks.

    So, how do you address the problem of mocking large loads of data collections like products with hundreds of attributes? Packing everything in YAML allows separation from code, but one immediately looses readability with numbers increasing – making this approach similar to just deserializing Varine_Object instances.

    Besides size – another issue is deep nesting of domain logic as in checkout logic, e.g. how do you mock checkout models?
    Or you are advocating for minimalistic approaches in test coverage only?

    BTW Any further thoughts about going beyond model level, i.e. into controllers (like mocking HTTP requests/responses as in Zend + Unit tests)?

    • Ivan Chepurnyi says:

      > So, how do you address the problem of mocking large loads of data collections like products with hundreds of attributes?

      YAML file can be created for each test, test can use more than one fixture. But anyway it will never use more then 10 or 20 rows of data. Otherwise, it is an alert for re-factor. I am keeping tests as simple as possible.

      > Besides size – another issue is deep nesting of domain logic as in checkout logic, e.g. how do you mock checkout models?

      The Main Problem in Magento architecture, that they haven’t used unit tests during the development. A lot of protected/private methods, that cannot be isolated by test… Yes you can call it via Reflection, but it is developed without thinking of possible tests on them.
      As for Checkout, I am testing my own customizations. If I create shipping method, then I create a mock object for rate request. If I create payment method, then I am mocking payment object and invoke my payment method API (authorize, capture, void, etc).
      If I create my own checkout, then I will cover it with tests. Magento team should cover their functionality with tests by themselves, otherwise they will never learn own code.

      > BTW Any further thoughts about going beyond model level, i.e. into controllers (like mocking HTTP requests/responses as in Zend + Unit tests)?
      Usual Magento controller looks like this:


      So for now I have not created any test cases for them. Magento blocks is some kind of mini-controllers, because instantiation of models goes there. So it makes sense to test them instead.

  2. Great tutorial, but I think that this is only for testing purposes…real problems comes on live site and you need to trace the issue in the good old-fashioned way when is no time for UnitTesting.

    For example, memory limit, you cannot test it in a proper way

  3. Hey Ivan, Thanks for this, it looks very interesting, about to check it out. I’m thinking that a really useful feature would be to have an option to generate YAML from an existing Magento database or tables. That way you could snapshot the state and use it for future testing. Does that sound feasible?

  4. Hi,

    first of all thank you very much for this wonderful extension! I found out that the following in your example did not work:

    phpunit --group models --group YouNamespace_Module UnitTests.php

    what worked for me was:

    phpunit --group models --filter YouNamespace_Module UnitTests.php

    is that the expected behaviour?

    • What version of the extension do you use? From MC or SVN QA branch?

      There was an error with grouping, but I fixed it about a month ago, but haven’t released it yet, it is planned for next release with additional functionality for controllers and layout.

      You can use QA branch from SVN for fixed version.

  5. katapofatico says:

    Hello, I see that it’s posible to load a fixture from another method on the same class.
    I try
    @loadFixture another_file_on_same_class
    @loadFixture another_file_on_same_class.yaml

    …and it does not work :(

    …and another question: Is there any way to load an external fixture from another test class?, something like

       * @test
       * @loadFixture
       * @loadExpectation [another_class+another_method_name].yaml?
       public function method(){...}


  6. Hello,

    first it’s a great extension, but it’s little difficult for me to set the configuration. I try to set a default customer over the fixture, but I always get a duplicate enry ’1′ for hey ‘PRIMARY’.

    May be you can give me example, how I can define a customer?

  7. Sorry, because I’m not an expert in phpunit. Maybe you have to give me more information about primarykey problem.

    I haven’t use provider or expetation for this test. I need a real customer-model for my tests.

    I build the fixture like your example with catalog/product and it’s working. But I can’t use customer/customer like this:

        - entity_id: 1
          entity_type_id: 1
          attribute_set_id: 1
          group_id: 0
          is_active: 1
    • Try to remove entity_type_id and set attribute_set_id to 0. Magento has bug related to attribute_set_id for customer.

      You need to specify primary because you may refer to customer by this id during writing code or using data provider with it. You may omit primary key standart tables, but EAV fixture cannot be used without primary key, because it saves entities in a batch query, for getting better test performance.

  8. WOW, that was the problem: attribute_set_id to 0

    Thanx for the fast solution!
    By the way. Is this the right place to post my problems?

  9. Your issue tracker is not available. Internal Error

  10. Thanks for the great article!

    I’m getting this error when I try to run a simple test with @test and @loadFixture

    data in fixture is good (like yours)

    PDOException: SQLSTATE[42000]: Syntax error or access violation: 1701 Cannot truncate a table referenced in a foreign key constraint (`unit_tests`.`customer_address_entity`, CONSTRAINT `FK_CUSTOMER_ADDRESS_CUSTOMER_ID` FOREIGN KEY (`parent_id`) REFERENCES `unit_tests`.`customer_entity` (`entity_id`))

    your help would be greatly appreciated! thank you!

  11. thanks for the response Ivan, I updated the code but still get this FK error. Here is the fixture:
    – entity_id: 1
    order_limit: 100
    – entity_id: 1
    customer_id: 1
    created_at: 2011-07-28
    subtotal: 100
    – entity_id: 2
    customer_id: 1
    created_at: 2011-07-29
    subtotal: 200
    – entity_id: 3
    customer_id: 1
    created_at: 2011-07-30
    subtotal: 300
    – entity_id: 1
    active: 1
    store_id: 1
    datefrom: 2011-07-01
    dateto: 2011-09-01

    here is the code:

         * tests the observer
         * @test
         * @loadFixture testObserver.yaml
        public function testAlwaysFlag(){
            $orders = Mage::getModel('sales/order')->getCollection();
            $customer = Mage::getModel('customer/customer')->load(1);
            $customer_session = Mage::getSingleton('customer/session');
            $checkout_session = Mage::getSingleton('checkout/session');
            $observer = new Copious_OrderScreening_Model_Checkout_Observer();
            $last_order = Mage::getModel('sales/order')->load($checkout_session->getLastOrderId());
            $this->assertEquals('Always Flagged.', $last_order->getOrderFlagNotes());
            // test checkout with flag set
            // check note
  12. Hi Ivan,
    is EcomDev_PHPUnit campatible to Magento 1.6?

  13. Hi Ivan,
    I’m working with your PHPUnit-Extension since a few weeks. Now I can sleep much better because I KNOW that my code works as expected. Thank you very much!

  14. Hi Ivan, thanks for the latest release, fixed my FK error! however, now I’m getting a ‘EcomDev_PHPUnit_Model_Mysql4_Fixture_Exception: Unable to clear records for a table “customer/customer”‘ error. I tried to google this issue with no luck. any suggestions? I have a simple test with

    – entity_id: 1
    order_limit: 100

    and in the test i’m just doing the following:

    $customer = Mage::getModel(‘customer/customer’)->load(1);
    $this->assertEquals(’100′, $customer->getOrderLimit());
    $this->assertEquals(‘’, $customer->getEmail());

    thanks for your assistance!

    • @guest2
      Seems like you should specify store_id, website_id for customer entity, they are required for Magento, because it has foreign keys for it.
      Also you need to use eav fixture key, since customer is EAV entity.
      try this one as fixture:

          - entity_id: 1
            attribute_set_id: 0 # Fix issue with customer entity load
            website_id: 1
            store_id: 1
            created_in: Default Store
            group_id: 1
            firstname: John
            lastname: Doe
  15. Attila Fulop says:


    I’m uncertain about the event-observer mechanism during unit tests.
    Should I expect them to work the same way as they’re “wired” in the Module config or they must be invoked explicitly as shown in guest’s example above?

      $observer = new Copious_OrderScreening_Model_Checkout_Observer();

    Thank you!

    • @Attila Fulop
      You need to test your configuration for being sure that it will be invoked in a particular case, and for observer module you need to emulate Varien_Event_Observer object that will be passed to your method.

  16. Attila Fulop says:

    Thank you Ivan,

    so it means that observers should behave the same way in unit tests as in the “real world”?

  17. Mario Peeters says:

    Is there somewhere online a how-to for writing controller tests for modules which reside inside the admin part?

    When I look at the response body I always get the html code for a login page. Is there a way to deactivate credential checking or make magento believe I am logged in?

  18. Hi Ivan,

    can you tell me how to set the customer/address into the fixture? I’ve tried different way, but I always get a Duplicate entry ’1′ for Key Primary.

  19. Attila Fulop says:

    @Mario: Did you manage to mock the admin/session? Which methods did you have to stub?

  20. Attila Fulop says:

    In a Model test I’m doing the following:

    $adminSession = $this->getModelMock('admin/session');

    And still get the errormessage:
    Mage_Core_Exception: Cannot complete this operation from non-admin area.

    Something has to be added or done differently, I guess.
    Did someone manage do get this working before?

  21. Sergey A. Lisenko says:

    Hi, Attila Fulop.
    Try this code, works fine for my tests.
    This is a adminhtml-controller example, i extend all my backend controller tests from this class.

    class My_Module_Controller_Adminhtml_Controller_Test extends EcomDev_PHPUnit_Test_Case_Controller
        const FAKE_USER_ID = 999999999;
        public function setUp()
        public function tearDown()
            $adminSession = Mage::getSingleton('admin/session');
         * Logged in to Magento with fake user to test an adminhtml controllers
        protected function _fakeLogin()
            $session = Mage::getSingleton('admin/session');
            $user = $session->login('fakeuser', 'fakeuser_pass');
         * Creates a mock object for admin/user Magento Model
         * @return My_Module_Test_Controller_Adminhtml_Controller
        protected function _registerUserMock()
            $user = $this->getModelMock('admin/user');
            $this->replaceByMock('model', 'admin/user', $user);
            return $this;
         * Test whether fake user successfully logged in
        public function testLoggedIn()
         * Test whether logged user is fake
        public function testLoggedUserIsFakeUser()
            $this->assertEquals(Mage::getSingleton('admin/session')->getUser()->getId(), self::FAKE_USER_ID);
    • @Sergey, thanks for giving such an example, maybe you would like to add this topic to the extension manual?

    • This class does not work.
      I installed as described in the (master branch) on GitHub.
      Using this class will result in the error “Cannot run controller test, because the host is not set for base url.”

  22. Attila Fulop says:

    @Sergey: Thank you very much Sergey, you’ve made my day! :) It works like charme

  23. Sergey A. Lisenko says:

    @Attila: Glad to help you. :) Anyway i still have no any ideas how test several dispatches of one controller actions in one test. Please, let me know if you have an experience how test it.

    @Ivan: Ivan, thanks for your great extension. Please, feel free to use any of my posted code of Magento unit tests as you wish.

  24. Shaun O'Reilly says:


    The following instruction at the top says:

    “And add your module to test suite configuration”

    Which configuration file are we talking about here, as I run out of guesses.


  25. Do you continue to use a phpunit config file other than the local.xml.phpunit? if I want to define my logging for clover coverage or junit or html, where would I do that at?

    • @James local.xml.phpunit is not related to phpunit configuration itself. It is used for Magento itself. You always can use your own phpunit configuration, just include this phpunit configuration file in command arguments.

  26. I’m trying to use a fixture to set a module enabled/disabled but it doesn’t seem to be working…am I doing this wrong?

    default/catalog/review/allow/guest: 1
    default/advanced/modules_disable_output/Mage_Rating: 0
    default/advanced/modules_disable_output/Mage_Review: 1

  27. Attila Fulop says:

    I’m testing a Model that invokes a model that is overridden in the module’s config.xml


    In the live scene, the overridden model gets invoked, but in the unit tests the default one. Is this normal, or is there any problem with the configuration?

    Thank you

  28. It wasn’t clear from the 0.2.0 manual how to add a test case for a controller. I was able to piece it together from hints in the manual, but one simple full example would be helpful.

    The key points were:
    - the test case should go in a directory called ‘Controller’ under ‘Test’ (I had put it in a directory called ‘controllers’ because I assumed I was supposed to follow the naming convention of my module one level up from ‘Test’, based on the example with ‘Model’)
    - the test case should subclass from EcomDev_PHPUnit_Test_Case_Controller instead.

  29. I had a problem with assertResponseBodyContains() as well – it claimed that the string was not in the body, then proceeded to dump the whole body (I could not find what made it dump the whole body when running the testsuite).

    I worked around that by adding:

    // FIXME: assertResponseBodyContains does not seem to work
    public function _assertResponseBodyContains($string)
    $body = $this->getResponse()->getOutputBody();
    $constraint = $this->stringContains($string);
    $this->assertThat($body, $constraint);

    in my own test class.

    I saw that this method wasn’t in the actual unit tests you have, so it may make sense to look into that?

  30. Nikolay Orlenko says:

    Hello, thanks for your extension.
    I have started to studding phpunit in Magento.
    Installed everything on virtual machine.

    XAMPP 1.8.1
    Apache 2.4.3
    MySQL 5.5.27
    PHP 5.4.7
    NetBeans 7.2.1
    Magento 1.6.2
    The current vesrion of EcomDev_PHPUnit from

    Added data to local.xml.phpunit

    After running phpunit from command line i have

    Fatal error: Uncaught exception ‘Exception’ with message ‘Warning: include(PHP\I
    nvoker.php): failed to open stream: No such file or directory in C:\_xampp\htdo
    cs\magento16.local\lib\Varien\Autoload.php on line 93′ in C:\_xampp\htdocs\magen
    Stack trace:
    #0 C:\_xampp\htdocs\magento16.local\lib\Varien\Autoload.php(93): mageCoreErrorHa
    ndler(2, ‘include(PHP\Inv…’, ‘C:\_xampp\htdoc…’, 93, Array)
    #1 C:\_xampp\htdocs\magento16.local\lib\Varien\Autoload.php(93): Varien_Autoload
    #2 [internal function]: Varien_Autoload->autoload(‘PHP_Invoker’)
    #3 [internal function]: spl_autoload_call(‘PHP_Invoker’)
    #4 C:\_xampp\php\pear\PHPUnit\Util\GlobalState.php(409): class_exists(‘PHP_Invok
    #5 C:\_xampp\php\pear\PHPUnit\Util\GlobalState.php(388): PHPUnit_Util_GlobalStat
    #6 C:\_xampp\php\pear\PHPUnit\Util\Filter.php(76): PHPUnit_Util_GlobalState::php
    #7 C:\_xampp\php\pear\PHPUnit\TextUI\ResultPrinter. in C:\_xampp\htdocs\magento1
    6.local\app\code\core\Mage\Core\functions.php on line 245

    Can you help me please?

  31. Hi, in the meantime we´ve transfered the PHPUnit tests used in Magento 2 to the current Magento versions. In them moment only a part of the tests can be used with the current versions, but you can download our approach on Github at Looking for some support for further development during the community… ;-)

  32. Hi – I’m hoping this extension is wonderful, but I’m having a problem from the start with the first phpunit UnitTests.php. I get the following:

    Fatal error: Uncaught exception ‘PDOException’ with message ‘SQLSTATE[HY000] [2002] No such file or directory’ in /Applications/MAMP/htdocs/magento/lib/Zend/Db/Adapter/Pdo/Abstract.php:129

    Any help would be great. Thanks!

  33. Hi,
    Thank you so much you just saved me some money and gave me knowlegde…
    Thanks loads and bless you…


  1. Tweets that mention PHPUnit and Magento? Yes you CAN! | E-commerce developers blog -- - [...] This post was mentioned on Twitter by Arnaud Ligny, David Voge, Sergej Braznikov, Toni Grigoriu, Alan Storm and others. ...
  2. Links 05/2011: Magento, e-Commerce, Online-Marketing & Mobile | Matthias Zeis - [...] die Erstellung von Unit-Tests für eigene Extensions erleichtert wird. Eine Einführung gibt es bei E-commerce developers.Die Agentur mü beschreibt ...
  3. Magento: When will the Unit Test Suite for Magento be released? - Quora - [...] with plenty of other ideas that seemed good at the time.There are always options on Magento Connect: a dynamic ...
  4. New Version Of EcomDev_PHPUnit 0.1.2 Extension For Magento | E-commerce developers blog - [...] who do not know about what the post is, here is the previous article about the extension: usual ...
  5. Tweets that mention PHPUnit and Magento? Yes you CAN! | E-commerce developers blog -- - [...] This post was mentioned on Twitter by Jan Brinkmann, Php Programmer. Php Programmer said: #magento and #phpunit marriage? ...
  6. :: [Magento] Testen von Configuration Backend Models - [...] nutze dazu die PHPUnit Erweiterung von EcomDev, die sich um das Ausführen der Tests, Instantiierung der Magento-Applikation, Test-Daten und ...

Leave a Reply

Your email address will not be published. Required fields are marked *


You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>