Blog Posts

Paris PHPCR meetup

 Last weekend I went to Paris to attend the Paris PHPCR meetup with about a dozen other people at the offices of CommerceGuys. Things started off with a short personal intro round, followed my presentations on PHPCR and PHPCR ODM. During the presentation there was a fair bit of discussion about the different available PHPCR implementations as well. Afterwards things split up in multiple groups working on integration of PHPCR with the Sonata Symfony2 Bundles, but also hacking on the Doctrine DBAL transport for Jackalope.

Overall I think the meetup managed to get quite a few new people up to speed with PHPCR, but more importantly was a great way to socialize with people around the common interest of CMS technology. Its always more efficient to communicate via email/irc etc after a bit of face time (not the apple kind). :)

As a bonus we even got progress on the code side of things and maybe got one step closer to bringing PHPCR to Drupal8, though its of course still too early to pop the champagne. Damien gave me the following topics to look into in regards to potential Drupal adoption:

- facetting

- query across version labels as a way to handle content staging workflows

- Jackalope Doctrine DBAL needs to be able to handle 10k+ documents with full text search on a shared hoster

- locking (pessimistic/optimistic locking, SELECT .. FOR UPDATE)

On facetting it should be noted thar I have already brought this topic up in the JSR-333 list.

Querying and versioning is something I need to look into, since I am very unsure if there is any support here. I will also try to find time to checkout HippoCMS and other JCR CMS and how they handle this.

For full text search in the Doctrine DBAL transport we discussed approaches briefly, but things might go in the direction similar to the full text search provided in Doctrine 1.x, which apparently is similar to how things are in Drupal today. Aside from this I am waiting for Damien to send a bunch of bug fixes that he worked on during the meetup.

On the topic of locking we are currently making progress in Jackalope, so I hope we can address the concerns soon.

Related Entries:
- News for the symfony2 cmf: Second PHPCR implementation, hackday announcement and PHPCR to become "official"
- Multilanguage support for Doctrine PHPCR-ODM
- How to manage patching a github hosted repo
- How to manage cache permissions in Symfony2
- Integrating Magento into Symfony2

Comments [0]

Key Dimensions of User Stories

 (Originally written by Thomas Botton and Benoît Pointet for the Scrum Alliance and posted at www.scrumalliance.org/articles/380-key-dimensions-of-user-stories)

Dealing with a large amount of user stories (more than your fingers and toes can account for) is not easy, most often they sit one after the other in your product backlog, or they are shuffled on a story map. Dealing with user stories is basically projecting them in a space of dimensions. The three essential dimensions to our scrum process at Liip are: the backlog order, the complexity (we call it effort), and the business value. Let's review briefly the first two and then focus on the third one.

Backlog absolute order

The product backlog order represents the urgency of a story. The absolute ordering forces the client to refine his understanding of the product backlog: there's only one single most important story to do next.

Story points

The complexity, is an integer value from the Fibonacci series, which represents a mix of technical complexity, work time, and even uncertainty bound to a story. Developers agree on it through the estimation poker game. Those two dimensions are commonly used in scrum teams around the world. The third one, business value, not so much.

Business value

Why isn't the business value that much used? Probably because scrum masters tend to forgive more to POs than to developers and because POs don't see the need to express this value. But business value matters - to everyone in the team. Even more, business value represents something that's deeper than the reason of the user story, something laying deep into the business logic: the added value that the story brings to the business.

But that's so hard to express. Really? Really harder than estimating the complexity of a story? Think twice! Or rather, think the same: complexity estimation is relative and summative. Business value estimation should be the same: estimate the value of a story relatively to the others, and resume this value in a single number.

You got it! We do estimate business value the same way the developers are estimating the complexity. We ask the PO and stakeholders to play estimation poker – and they love it! It's a unique opportunity for them to get together and come up with an agreement on story relative values.

Applying the same rules on estimating business value and complexity has an important side-effect: it brings understanding and respect in the project team. A PO will be more likely to understand why some stories need to be reestimated by the developers - and vice versa. Using a numerical scale for the business value also allows to visualize it (cf. chart below), something MoSCoW can offer in a limited way - if at all.

As an example, here's such a chart for the stories of sprint 1 (green), sprint 2 (blue), and the remaining of the product backlog (grey) of a small project we started recently. You clearly understand that stories laying at the top-left corner are the most interesting, which is why we took them in our first sprint.

The ROI (a.k.a Return On Investment)

Such a chart brings to your scrumish minds a sense of "Return On Investment": the ratio between the business value and the complexity (aka. BV/SP). Since both are relative metrics, this is a relative return on investment that you'll never be able to translate into dollars, but which allows you to compare user stories. On the following chart, ROI is increasing in an angular way. It is now fairly easy to spot the undone stories (grey) with high ROI: 8, 20 and 30. Those are probably the ones that the PO will put at the top of his product backlog.

Those three dimensions are very flexible until sprint commitment. However, right before it, both the complexity and the business value must have been estimated for the top of the product backlog, which means that the product backlog has been re-ordered! The classical sprint commitment can then occur: the team pulls the stories from the top of the product backlog into the sprint backlog, negotiating order and amount with the PO. After commitment on a story, both its complexity and business value should not be altered, since it would tweak the metrics.


Comments [0]

Niwea Application Development - First experiences

Since quite a while now we're developing a NIWEA based app for one of our clients. If you don't know what NIWEA is you can read our earlier blogpost.

It was a very interesting new challenge. At first I thought cool, now I can use all the fancy HTML5 and CSS3 things I read so many articles about. Later I realized that creating a native-looking app with just web technologies is not all sunshine.

There are a few issues you may experience that are typical to webapps. These concern:

  • - Rotation & Calculation issues
  • - App Resume
  • - Offline availability
  • - Singlepage Problem

Rotation & Calculation

Whenever you rely on winHeight for your overlays, widgets, flyouts or lightboxes you will run into trouble. This caused by the constantly changing window height in iOS.
There are 4 possible states you need to be aware of:

  • - URL bar hidden
  • - URL bar out
  • - URL bar overlapping (during a request)
  • - Carrier bar overlapping in Add2Homescreen (standalone) Mode
All those situations may occur in landscape and portrait mode. Therefore there are 8 different heights & widths you need to be aware of and test your calculations with. Now you could just hardcode values for those 8 possibilities but that's not a good plan if you also want to support Android, WebOS and Win7 Phones.

A generaly good advice is to use :
 window.scrollTo(0, 1) 
before you're doing any calculations that rely on winHeight. This will hide the scrollbar and reduce the possibilities.
When you're in standalone mode you can just subtract 20px from your winHeight and add 20px padding to your content div.

 if (true === window.navigator.standalone) {

     winHeight -= 20;

     Y.one(#content).setStyle('marginTop', '20px');

 }

Rotation

All the places where you use calculated values need to be rotation aware. That means whenever the device rotates, you need to recalculate. This is best done by listening to the orientationchange event. Most devices also fire the resize event, but in my experience it didn't prove very reliable. Sometimes the phone is too busy and it doesn't fire any event at all. This bug has been resolved in iOS 5.

Singlepage

Whenever you're in standalone mode and click on a link, it will open in Safari. I'm not sure what Apple thought was the use of that but it's one of those problems you just need to fix.
To avoid this issue you can create a CSS class that is applied to all links that should not open in Safari. Add a delegate event handler that is listening to all click events on those elements.
Whenever a link is clicked, prevent the default behaviour with e.preventDefault(); then extract the URL and request the content through Ajax. That way Safari does not open.
Another workaround is to do POST's with hidden forms. This is helpful for logouts or other things you want emediate redirect and don't want to wait for the Ajax response to bounce back.

App Resume

We're used to multitasking and we expect apps to stay in the same state as they were when we left them. This works great if you use your NIWEA App inside Safari. In standalone mode it works different. Whenever you close or switch to another app, iOS forgets about the state of your app. Once you relaunch or switch back it will reload the entire app. Not on the last active url but on the url it was bookmarked too. The solution to that problem is simple. Whenever you request an url in your Singlepage code, put the last active url into localStorage. Once the app relaunches, check if a url is in localStorage before loading the starting page. This makes it necessary to have a so called loader Seed which is a construct that holds the navigation elements but loads the content after the initial load is done. The loader seed is also helpful if you want your app available offline.

Scrollviews

There are some nice libraries to make a scrollable div for mobile devices. You might ask yourself why not just do overflow: scroll. It does indeed work, but you will have to scroll it with 2 fingers. One of the most famous library is iScroll.

If you don't care for iOS 4 there's a very easy alternative called overflow scrolling:
 .scroll-x, .scroll-y { 

     -webkit-overflow-scrolling: touch; 

 }
This will allow you to have the element scrollable with 1 finger.

Offline availability

The concept how we solved the offline concept would enlarge this blogpost way to much. I already described what we did there on my blog. If you're interested check it out there.

Tools

Simulator

The obvious one is the iOS Simulator. It comes in very handy when you need to check your application on iOS 4 and 5. You can easily switch between versions and devices. Also you can reset the entire device. This is very useful if you want to clear the cache and the local database. The Simulator comes bundled with XCode. Once you've installed XCode, navigate to /Developer/Platforms/iPhoneSimulator.platform/Developer/Applications/iPhone\ Simulator.app and put the App into your Dock.

iWebInspector

This tool allows you to inspect your html inside the Safari of your Simulator. You can change classes, edit markup and access the local databases. It's also possible to access the apps in standalone mode. Basicly you can do everything you can do in the Chrome Developer Toolbar.
iWebSimulator is available for free on http://www.iwebinspector.com/

Retrospective

All in all is it a nice challenge to develop NIWEA applications. It's nice to use new technologies and face problems that differ from the usual "it looks weird in IE" problems. But what you and your customer need to be aware of is that it's not going to be as fast as a native app out of the box and some things will need more time than in a native app. What you get in return are multi-platform capabilities with much less effort than the "traditional" native approach.


Comments [0]

What Liip did after winning the IKS semantics UX contest

As you maybe have heard, we won the IKS Semantics UX contest a few weeks ago (see this and this for more details). We of course we're not lazy in the meantime and integrated Apache Stanbol into this editor.

Loïc wrote a little summary on the IKS blog what we already did and what's coming in January 2012. Go and read it here. The most important part: There's a hackaton for merging all this with the excellent CreateJS (which was on Hacker News lately) from 5 to 7 January at our Liip office in Zurich. You're all welcome to take part, more details and subscription at lanyrd.com/2012/createjs-hackathon/

Related Entries:
- Symfony CMF hackday october 22nd in Cologne
- A frontend editor for Symfony2 CMF with the help of VIE
- Progress on PHPCR with a hackday
- Multilanguage support for Doctrine PHPCR-ODM
- A redesign of *.liip.ch. Plus the all new RocketLab

Comments [0]

Modular Asset minification

One of the most effective ways to boost the application's frontend rendering is to minify the assets (CSS and JS) into single files and set a long cache-time so that they are not requested each time the page loads.

So, most of the times, the page loads one big CSS and one big JS containing the style and the js needed by all the pages.

The problem

I noticed how many times the loaded asset is not efficiently used since most of it affects elements not present in the current page.

To get an idea just install Page Speed and check the "Remove unused CSS" in the report.

The "wasted" CSS is often around 50%.

Modular assets minification

I started wondering if  a different, modular, approach would fit better in some circumstances.

By "modular" meaning that each page only loads its own minified assets which are loaded at every request.

The playground

I have quickly set up a simple "test suite" to see if the modular approach pays.

To measurements have been done using the above mentioned Page Speed and Firebug on Firefox 7 on a quite fast Linux powered machine.

Also note that results depend on many factors so they always differ and are not meant to have any scientific value.

Anyway this is what I got:

All CSS minified

  • Wasted CSS: 50%
  • Load time with empty cache: 289ms
  • Load time with warm cache : 133ms

Page specific CSS minified

  • Wasted CSS: 26,3%
  • Load time with empty cache: 267ms
  • Load time with warm cache : not applicable since the css will always be requested

Conclusions

Even the test has some limitations it clearly displays that to balance the additional time needed by the browser to render the big CSS file, the waste percentage has to be very high.

I have a bad feeling knowing that a great part of the CSS is not used, being thus inefficient but the "cold numbers" say I don't have to worry about this.

What do you think?


Comments [10]

Multilanguage support for Doctrine PHPCR-ODM

Over the last weeks, Dan, Brian and myself worked on adding translation capabilities to Doctrine PHPCR-ODM. PHPCR-ODM is an object - document mapper for the php content repository (PHPCR). Thanks to the Liip Ecostar process, we got funding to do this during work time.

How does it work?

Using persistTranslation($document, $locale) or persist($document) with the @Locale annotation allows to store several copies of the "same" document in different languages. You update the document for the next language and then persist that translation too. Using find() to get a translated document, the LocaleChooserStrategy class will find the best (according to its implementation - might look at session locale, browser preferences, user account settings, ...) available translation for a document. And using findTranslation() you can explicitly specify which language you want.

There are two translation strategies available: Store translated fields in a separate namespace as properties of the same node, and namespaced child nodes per locale with the translated properties in them. You can add your own strategies with the DocumentManager::addTranslationStrategy() method. Translation always happens on a document level, not on individual fields to keep performance reasonable.

Our translation strategies use a namespace to avoid collisions with other attributes resp. child nodes. In order to use multilanguage, set up the console and run

php bin/phpcr doctrine:phpcr:register-system-node-types
.

When using findTranslation, the existing document instance is updated rather than a new one created. Otherwise we could get into non-deterministic situations when you update non-translated fields on two objects.

A complete example how using the translations looks like, using the default configured LocaleChooser:

use Doctrine\ODM\PHPCR\Mapping\Annotations as PHPCRODM;

/**
 * @PHPCRODM\Document(alias="translation_article", translator="attribute")
 */
class Article
{
    /** @PHPCRODM\Id */
    public $id;

    /**
     * The language this document currently is in
     * @PHPCRODM\Locale
     */
    public $locale = 'en';

    /**
     * Untranslated property
     * @PHPCRODM\Date
     */
    public $publishDate;

    /**
     * Translated property
     * @PHPCRODM\String(translated=true)
     */
    public $topic;

    /**
     * Language specific image
     * @PHPCRODM\Binary(translated=true)
     */
    public $image;
}


$localePrefs = array(
    'en' => array('en', 'fr'),
    'fr' => array('fr', 'en'),
);

$dm = new \Doctrine\ODM\PHPCR\DocumentManager($session, $config);
$dm->setLocaleChooserStrategy(new LocaleChooser($localePrefs, 'en'));

// then to use translations:

$doc = new Article();
$doc->id = '/my_test_node';
$doc->publishedDate = new \DateTime();
$doc->topic = 'An interesting subject';
$doc->image = fopen('english.jpg');

// Persist the document in English
$this->dm->persistTranslation($this->doc, 'en');

// Change the content and persist the document in French
$this->doc->topic = 'Un sujet intéressant';
$doc->image = fopen('english.jpg');
$this->dm->persistTranslation($this->doc, 'fr');

// Flush to write the changes to the phpcr backend
$this->dm->flush();

// Get the document in default language (English if you bootstrapped as in the example)
$doc = $this->dm->find('Doctrine\Tests\Models\Translation\Article', '/my_test_node');
echo $doc->topic;

// Get the document in French (updates the existing document)
$this->dm->find('Doctrine\Tests\Models\Translation\Article', '/my_test_node', 'fr');
echo $doc->topic;

What was added?

The complete overview is in the pull request. Just a summary:

  • New annotation options:
    • translator=attribute|child|your_own is a new attribute for the @Document annotation, to mark a document as being translated and specify how translations are to be stored
    • @Locale makes a class property hold the locale the document is currently translated to.
    • translated=true is a new attribute for all properties to mark the String, Number, Binary and so on as being translated.
  • TranslationStrategy is used for the translator attribute of the document. Default strategies for attribute and child exist.
  • LocaleChooserStrategy is used to determine what language matches best for doing language fallback, with the possibility to do language fallbacks based on your self defined logic. The default strategy is configurable with an ordered list of locales per requested locale
  • DocumentManager::persistTranslation($document, $locale) save document in that language
  • DocumentManager::findTranslation($className, $id, $locale) load a document in the specified locale (instead of the default one)
  • DocumentManager::getLocalesFor($document) (get the list of locales this document currently exists in)
Related Entries:
- News for the symfony2 cmf: Second PHPCR implementation, hackday announcement and PHPCR to become "official"
- Paris PHPCR meetup
- What Liip did after winning the IKS semantics UX contest
- How to manage patching a github hosted repo
- How to manage cache permissions in Symfony2

Comments [0]

6 good reasons to work at Liip

Seven years ago I started working as a developer at one of the two companies that merged together to become Liip.  Why have I stayed so long?  I still like developing, and I still like working here.  Here are a few of the things that I think make Liip a great place to work:

Great people

There are lots of friendly, smart, helpful, interesting people here who are good at what they do. 

Interesting work

Projects come and projects go, and with each project I learn something new.  It's said that variety is the spice of life, and I certainly enjoy a bit of it in my work life.   One of the beauties of open source software is that it can be changed to do something more than before, and this can then be made available to others.  I always get a good feeling when I'm able to contribute something back.

Continual learning

Besides the new technologies or new corners of an old technology that we often explore in the context of a project, Liip provides it's employees other educational opportunities:

Once or twice a week there is a "tech talk" - a 30 minute presentation of some interesting topic.

Monthly hackday - one day in the month is kept clear of other meetings, so that interested people can participate in one of the sessions that has been organized.  Usually the goal is to bring an open source project a bit further, but sometimes it's just about exploring - hands on - an interesting topic.  For more, see reports of some previous hackdays.

Project-related workshops: sometimes, we need to know more to find out the best way to do something for a project.  In this case the team working on the project can organize a special workshop to deepen their knowledge on the subject.

Yearly "Tech day" - a day for and by the whole company, full of presentations and workshops (and good food).

An individual time and money budget for education that can be used as wished (well, not for a ski vacation).  Examples are conferences, language courses, trainings, or participating in the monthly hackdays.

Take a look at rocketlab.liip.ch to see some of the upcoming events, and please join us for one of them if you're passionate about the subject.

Family friendly

I saw a postcard the other day that said "Erziehung ist ... Zeit haben" (Raising a child is ...  having time).  I work at 80% so that I can spend one day during the week with my kids.  This is frowned upon in many companies, but Liip is very open to it; many people here work at 60-90%.

Also unusual for this country, Liip gives new fathers four weeks of paternity leave.  These four weeks can be taken sometime in the year after birth.  For me this was a great opportunity to bond with my kids, and my wife was really happy to me around to help out in the weeks after birth; this made her life easier.  Maternal leave is also augmented a bit, apparently the calculations there are a bit more complicated so I can't say by exactly how much.

Company with a conscience

Liip reserves the right to, and sometimes does, refuse projects because we find the goal of the project unethical.  Liip also takes measures to be environmentally friendly; examples are using "green" electricity and doing CO2 compensation for the energy used by the company.

Zen of a growing business

Liip has grown quite a lot in the past few years.  This has required a number of organizational changes, which, imho, Liip has succeeded at remarkably well, with few growing pains.  There are some organizational structures, but it remains transparent, uncomplicated and efficient.  It is not a hierarchical, decisions-come-from-the-top-live-with-it kind of place.  Constructive discussion of important topics is encouraged.  It's a company that keeps it's eyes and ears open and adapts.

And ...

Well, this post is getting long, so I'll stop.  But I certainly didn't mention all of the reasons that Liip is a great place to work.  If you're interested in working with us, take a look at the currently open jobs.


Comments [0]

Scrum Breakfast Dezember 2011 in Zürich

Last week I presented Scrum@liip to a very interested audience. 40 people at 8am was really impressive. It was great to see how people feel that we really pushed Scrum to a very high level. Even if we sometimes feel that we could do even more and faster. But all is very relative... below an extended version in German. Here you get to the slides.

Die Einladung von Peter Stevens zum Thema "Scrum in der Firmen-DNA" habe ich gerne angenommen und letzte Woche gut gestaunt, morgens um 8h rund 40 Interessierte in der Industrieperipherie von Zürich anzutreffen. Offenbar hat das Thema viele angesprochen, denn entsprechend war auch der Austausch, der bis Mittag in hoher Intensität angedauert hat.

In der eigenen kleinen Welt kommt manchmal das Gefühl auf, mit den angestrebten Anpassungen - die natürlich nie aufhören - an Ort und Stelle zu treten. Macht man dann bei Gelegenheit einen guten Schritt zurück und präsentiert den aktuellen Stand einem interessierten Publikum wird deutlich, zu welcher "Güte" wir Scrum bei Liip mittlerweile getrieben haben. So ist das konsequente Besetzen aller Rollen ohne Personalunion heute selbstverständlich, die Methode innerhalb der Firma absolut unbestritten und die Zusammenarbeit mit dem Kunden transparenter denn je.

Feste Teams mit etabliertem Flow und beständiger Velocity und die Konzentration auf Business Value befähigen uns heute, Projekte von Beginn mit maximiertem ROI (Return On Investment) anzupeilen. Mit traditionellen Methoden ein Ding der Unmöglichkeit.  Dazu notwendig ist aber auch eine Firmenkultur, die den Umgang mit jedem Individuum im Sinne einer Meritokratie und nicht verlockend herkömmlich als Hierarchie lebt. Viel davon ist wohl durch unser langjähriges Open Source Engagement sowieso fest verankert.

So freut natürlich die Einschätzung von Peter, die kaum völlig falsch sein wird:


"My first external training and coaching customer was Liip AG. I gave them their first introduction to Scrum back in 2007, and helped them with some additional training and retrospectives over the years. In no company have I done less and seen more come of it than at Liip (my suspicion is that they already had Scrum Values in their DNA). In any case, they are growing rapidly and organically while going from one Master of Swiss Web to the next."

Und hier noch die - nicht ganz Corporate Design kompatiblen - Slides.


Comments [3]

How to manage patching a github hosted repo

Here at Liip we obviously try to reuse all the good stuff in the Symfony2 and PHP community. So instead of reinventing the wheel when we find that a feature is missing we just fork, patch and submit our changes upstream. So far so simple.

The problem is of course that this entire process takes time during which our projects need to move on. As a result there is a time period where most likely our project will need to use the fork. I must say I am still not entirely happy with the workflow I am going to describe here. So it is definitely still a work in progress and needs refinement and we are only partially using the below described approach. Hopefully feedback to this post will get us to a perfected approach.

1) fork the project

Make sure to fork to an organization that co-workers can reach. Usually we fork to the Liip organization. If we have client developers on the team we fork to the clients organization.

2) update the description of the fork

Make sure to declare this fork as transient, so that nobody gets the idea that this fork will stick around. Github's repo description can be modified without changing anything in the repository.

3) create a feature branch with your changes

Committing to master would seriously break the workflow. Never assume you will only need one feature changed and mixing features will make it harder to get your features merged upstream. Never assume that upstream will accept your patch, they might go with someone else's patch. Never assume that your PR actually makes sense, sometimes after submitting your PR you realize that it was actually a bad idea and you should have taken a different approach.

4) create a ticket on your fork for the branch

This ties in with 2). Anyone using this branch should subscribe to that ticket. The ticket should also reference the PR. This way it is later easily possible to alert anyone that cares that the PR has been merged upstream and that you are going to remove the branch in X number of days.

5) submit a PR

6) update your project to point to your feature branch

At this point you need to make sure that all relevant people from your project are subscribed to the branch ticket.

6) once the PR is merged switch point your project back to the original repo

7) update the branch ticket that you are about to remove the branch in X number of days

8) remove the feature branch

9) if there are no more feature branches, remove the repo

Since nobody should depend on your master it should be safe to remove the repo once all feature branches are removed.

Note in some cases you might need multiple features at once. In that case create a separate feature branch for each feature and create another feature branch that combines all the features you need.

Also its best to make sure that upstream addresses your PR in a timely manner. Otherwise more and more people will start relying on your fork, you start to forget what it was about and more importantly, you might become incompatible with upstream. Making sure that your PR has a good description, tests and documentation will go a long way to getting your PR merged sooner rather than later. A friendly nudge now and then can also help to get things back on the radar (*nudge* Kris *nudge* *nudge*).

If all else fails and you are unhappy with the way upstream handles the merging or you want to take things into a different direction, do a for "real" fork. At this point you can start merging into master, but be aware that getting upstream features will now become potentially messy. So far we have only done this in one case where we forked the AvalancheImagineBundle to the LiipImagineBundle since we need additional flexibility that Bulat didn't want to add to his bundle.

Related Entries:
- Integrating Magento into Symfony2
- Paris PHPCR meetup
- Multilanguage support for Doctrine PHPCR-ODM
- How to manage cache permissions in Symfony2
- Packaging solution for (php-)projects

Comments [1]

Behat hackday

A few days ago, we held an open hackday on doing Behaviour Driven Development (BDD)  with Behat at Liip Fribourg. It was the opportunity for the handful of participants to get a first grip on Behat and explore some aspects of BDD through it.

Introduction

BDD is an interesting practice for agile web developers, it ideally allows the formalization of the acceptance criteria of a story through scenarios written in Gherkin, a domain specific language that is human writable and machine readable. An archetypal Gherkin feature with one scenario (from Behat documentation):

Feature: My feature
      As an explicit system actor
      I want to gain some beneficial outcome which furthers the goal
      So that I realize a named business value
      @some_tag @some_other_tag
      Scenario: Some determinable business situation
          Given some precondition
              And some other precondition
          When some action by the actor
              And some other action
              And yet another action
          Then some testable outcome is achieved
              And something else we can check happens too

Gherkin Scenarios are composed of steps, and subordinated to features. Scenarios can be tagged, to later specify which subset to test, or to put constraints on their testing. The actual semantic of steps is implemented in Contexts (PHP Classes).

Explorations

Sylvain and Thomas started integrating Behat in one of their existing Symfony2 project. They appreciated the effectiveness and the ease of use of Behat+Mink to test form interaction, and the BehatBundle for Symfony2 which made integrating Behat into Symfony2 a breeze. They outlined that the first steps to implement are the ones specifying the role of the user: "As an editor ...". At the moment this is not used during the test but it could be interesting to use this feature to authenticate the user before running the test.

Donato mainly tested the Mink SahiDriver against liip.ch's project bundle and, outside Symfony, on a simple FuelPHP based application. In both cases the Behat+Mink+Sahi combo proved to be very handy and ran smoothly. The tests with Sahi were run successfully with both Firefox and Chrome.

Adi played with remoting sahi in order to start tests inside a virtual machine but have them executed in the browser on a remote system for testing with different browsers on different OS.

Lukas aimed at testing Ajax requests. He outlined the limits of testing asynchronous process through Behat.

Timo and Benoît focused on the product owner interests in the system. They went through some existing sprint review protocols (made of demo scenarios) and analyzed how much of them could actually be transformed into Behat features and scenarios. They found out that currently Behat, through Mink and the various browser drivers, allows to test only for content presence, and some browsing and navigation interactions. Still a lot more standard steps might need to be provided to test for the presence of document header elements, element attributes, element styling, element metrics, document validation, mouse hovering, scrolling, element visibility ...

Conclusions, open questions

BBD and Behat are certainly the easiest way to introduce test-driven development on a project.

One of true value of BDD lies in the ecosystem of scenarios steps provided out-of-the-box: only with a broad spectrum of steps covering most aspects of browsing will Behat be effectively usable for a PO.

BDD empowers the product owner, but he still has to closely collaborate with developers when writing acceptance criteria: the syntax has to be respected, the PO may not know how to test for an element presence, etc.

It is yet undecided how BDD concepts match agile concepts. A user story will be covered by many scenarios, but should a Gherkin feature represent only one user story? or should we rather use tags and match features to epic stories or even code parts (Symfony2 bundles)?

Despite the shortcomings or difficulties in the Javascript and XHR area we're planning to get started with BDD in an existing project as of mid-December. It will be a slow start and we will grant ourselves some learning time. Depending on how practical the approach will prove in our context, we will not limit ourselves to new user stories, but also describe a number of existing features and get them tested this way.


Comments [1]

Next1-10/1164