New Version Of EcomDev_PHPUnit 0.1.2 Extension For Magento

There are just 10 days were gone and we are happy to introduce new version of our Magento Unit Test suite. In this version were added full multi-store support and EAV fixtures, was improved the way of expectations retrieving and some other minor fixes.

As you see from the above example, eav is a new fixture type, catalog_product is an EAV entity type code. Each row represents a set of attribute values with special codes for multi-store values, /websites and /stores. If you want define a value for the attribute in a particular store or website you need place this value inside of /stores or /websites element by specifying store or website code with attribute code. For instance, if I want specify description attribute for german store, it will look like this:

# .. other attributes
/stores
german:
description: ---|
Some description text in German
Language that has a lot of
lines....

Also you can extend EAV Fixture loader by adding custom loaders for entity types, you just need create a resource model and extend it from EcomDev_PHPUnit_Model_Mysql4_Fixture_Eav_Abstract, implement your custom process of load and add your resource model class alias into configuration:

By the way, with this configuration node you can override current loaders as well. If there is no custom loader specified, then ecomdev_phpunit/fixture_eav_default will be used.

For EAV entities, after loading of the fixture, can be run related indexers, so if you do not need any of them you should use @doNotIndex [index_code] and @doNotIndexAll annotation in your tests doc comment. It will speed up the tests evaluation process.

Websites, Store Groups, Stores…

Now you can setup any number of them for the test, because scope fixture type was added in this version. IT works pretty simple, just specify standard fields from that models:

Notice: Place fixture types data inside of the fixture file in the same order as it should be loaded. Because if you create website in the end of file, but will use its ID in at the beginning, you will get a fatal error.

Applying Store Scope

If you want to test behavior of your custom module for a particular store, then you can apply it by calling EcomDev_PHPUnit_Test_Case::setCurrentStore($store) method inside of your test.

Improved Expectation Usage

Now it became more easy to use expectation for your test case if you have a lot data provider variables. Now you can load some part of fixture as Varien_Object and use it. For instance, you have to test different products on different stores but via single logic. Expected results and input data of course is very various. So first of all you will create a data provider, that provides data for your test:

If you noticed, now you can specify additional arguments to EcomDev_PHPUnit_Test_Case::_getExpectations() method. The first argument represents a format for sprintf, which will be used for formatting the other arguments. If only one argument is specified, then it will be used as constant value.

Small Example

In conclusion, would be great to give you some ideas how you can use it all together. Here is small test case for product, where is implemented test for multi-website price and price indexers functionality. Do not forget to create Example module, like explained in the previous article.

P.S.: Ongoing Versions

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.

18 Responses to “New Version Of EcomDev_PHPUnit 0.1.2 Extension For Magento”

The extension is pretty cool, but I have a small problem using it. I have a task which is related to modifying exact eav attribute table (adding new columns and special logic related to it). To test it I write a fixture like eav/attribute: – attribute_id: 999 entity_type_id: 4 attribute_code: xxx frontend_input: select my_new_added_field: yyy

This works great, but the after finishing it truncates the eav_attribute table and I loose all the system attributes. So I cannot write a simple eav fixture for catalog_product like in your examples above, b/c name, status, visibility, etc do not exist anymore. Here I have a question to you: is there any workaround in your mind? I guess there is one – create all the required attributes in the fixture file before catalog_product, but I believe that there should be better solution.

By the way, I am not sure that attributes should be added via fixtures, since it is the eav metadata, that should be managed via install scripts of your module. It is like adding a table column via fixture.

Yes, I agree that attributes should be added via installers, but imagine the following situation: I need to add new column to all eav attributes (like eav attribute’s attribute). And then I need to write special logic, let’s say depending on this column and attribute frontend input type. My module does not add any new attributes, they will be added later in the admin by store administrators, and they also will set the value to my column. But I need to test the new logic. My column might have different values and this should be tested, so I need to write fixtures for attributes with different values of new column. See what I mean?

Hello David, so you need a mechanism for updating a particular value in attribute instance? right? Then it is easy to solve by updating value in setUp method and revert it back in tearDown, without database modifications, since Mage::getSingleton(‘eav/config’) caches attribute objects in memory. You can do it in your test case or create new fixture type by extending EcomDev_PHPUnit_Model_Fixture. Also if you want this new feature in the next releases you can submit a feature request.

As for custom attributes via fixtures, I will think over this idea, but it will be too bad for unit tests performance, because it will require reinitialization of entity type singleton for each test.

And what about about using of mock objects for your custom logic testing? Is it not fit your requirements?

Yes, your idea about in-memory attribute should work, thanks for the advice.

Also I have an idea for the future release which may help to solve such issues – maybe there should be an ability to set an annotation to the test case which will tell the framework that it should not truncate fixtures tables, but revert to the initial state. Yes, this will be slower, but for one or two specific test cases this might be useful. I’ll also post it to you issue tracker.

Hi Ivan, Is it possible to test observers with your extension? I have several doubts about this point: 1. how to check that it is triggered? Could I dispatch the event I’m listening to from within my test case? 2. how can i pass the $observer variable to another methods, in another class? Can I setup a fixture? Thanks in advance :)

1. You should invoke your observer method in the test case. For config.xml files configuration I am planing to create assertion methods, like $this->assertEventIsHandled(‘area’, ‘eventName’, ‘module/observer’, ‘methodName’) for testing of setting events properly, finally I have some time to improve the extension, had a lot of project work last month…

2. Actually I think it will not be a problem for you to create an observer instance, but I will add some kind of factory method for observer object creation from passed event data.

Thanks for such an awesome Magento module! I’ve got the question about mocking objects with you module. My project uses a lot of externals API’s with custom modules and I would like to mock some of them when running tests. Do you have any idea or some kind of author’s vision how can that be done?

As I figured out you are replacing the config model with your own via Reflection. Maybe that config model could be used to return the mocks instead of the real class names when instantinating the class via Mage::getModel or with Mage::helper? Also I was thinking about defining what should be mocked on module basis. I mean I don’t need mocking of API when testing the module itself, but I would gladly mock it when testing some other module.

What do you think? I would gladly elaborate the feature just to be sure I’m doing it correctly so that I could be merged into the upstream.

Ok, maybe I can create some sort of method that will temporary replace calls to Mage::getConfig()->getModelInstance() and other factory methods as well. Please submit it as feature request to our issue tracking.

I was curious if you could give an example of how to use a fixture to test an order? Make sure the order has the correct shipping/billing address along with the correct items. I’m running into problems when I use a fixture to load up order data.

Hi Ivan, thanks as always for this. I’m keen to develop a unit test that can verify the adminhtml ACLs for a module match the adminhtml menu and actions. Can you suggest the best way to use EcomDev_PHPUnit_Test_Case_Config or other assertions to test this? Thanks, Jonathan

Thanks for nice feedback. I think it is nice addition for an extension, but I don’t have time now for creating a new constraint. But you can easily make it yourself and make a pull request. The class you need to extend from is EcomDev_PHPUnit_Constraint_Config_Abstract and inside of EcomDev_PHPUnit_Test_Case_Config you need to use not Mage::getConfig() as actual value for check, but you should apply constraint to Mage::getSingleton(‘adminhtml/config’).

Hi, this is a very dumb question, but after all this, how do i “run” the test? do i just run command “phpunit Product.php” ? In which case I get error PHP Fatal error: Class ‘EcomDev_PHPUnit_Test_Case’ not found in /var/www/app/code/community/EcomDev/Example/Test/Model/Product.php on line 7