I spent Christmas at my grandmother's house in Florida this year. She very generously agreed to let me devise the menu for Christmas dinner, and I decided that sticky toffee puddings should make a second appearance in as many weeks.
Now, at home, I work with a KitchenAid stand mixer, a piece of equipment that has become pretty much standard issue for home cooks - and, particularly, bakers - these days. Nonie (that's my grandmother), however, never saw a reason to replace her vintage-fabulous Mixmaster.
At first, I was frustrated - the bowl was too wide to cream the single stick of butter and 2/3 cup of sugar effectively, and I ended up doing things by hand. Then I discovered the second (deeper and narrower) bowl - aha! So, the lesson? Always explore the depths of the cabinet before giving up, and never assume that something as adorable as the Mixmaster can't be practical as well.
As mentioned at the end of my last post, i continued exploring the idea of having a custom homepage with content aggregated from different sites on the client side. I tried using the about:blank page of Firefox as the starting point on which to build content, but could not succeed. After googling on this issue [...]
Hope you are having great holidays, spending some good time with your family & friends, and be able to reflect on 2008 : )
I certainly had.... last few days have been really great. It has been a while since I have spend some quality time with my family & friends... especially my son( 5 year old).
It is going to be busy (in a good way) two weeks ahead for me. I am going on a cruise tomorrow, and next week I will be in San Francisco for a Wharton workshop on "Development of Web-Based Products and Services" conducted by Prof. Karl Ulrich. I am really exited about both.
Details on the workshop to follow....
Wish you all a very Happy New Year!
Hope you are having great holidays, spending some good time with your family & friends, and be able to reflect on 2008 : )
I certainly had.... last few days have been really great. It has been a while since I have spend some quality time with my family & friends... especially my son( 5 year old).
It is going to be busy (in a good way) two weeks ahead for me. I am going on a cruise tomorrow, and next week I will be in San Francisco for a Wharton workshop on "Development of Web-Based Products and Services" conducted by Prof. Karl Ulrich. I am really exited about both.
Details on the workshop to follow....
Wish you all a very Happy New Year!
I just saw this review by Roadfood's Jane and Michael Stern on NYTimes.com. Seems there is a new biography of the Widow (in French, Veuve) Clicquot that focuses "as much [on] Champagne itself as [on] the woman who helped elevate it to celebrity status."
Veuve is admittedly not my favorite Champagne, but Barbe-Nicole Clicquot was instrumental in turning Champagne into the international commercial enterprise it is today, and I can't help but be fascinated by that.
Sounds like my perfect book, and available at Barnes & Noble for less than 20 bucks (if you're a member, that is). Score!
How did I become famous, you might ask? Well! Schmap puts out these nifty online guides to different cities (which are absolutely perfect for use with your iPhone), and they've used two of my photos for their latest Paris edition!
A shot I snapped outside of Pierre Herme's (closed) Rue Cambon shop is included, as is the photo I took of the entryway at Angelina's. Schmap links directly back to my Flickr photostream, so here's hoping Schmap will earn Queenie some new Paris-loving readers - no doubt Schmap will earn some Queenie-loving users.
Thanks, Schmap!
P.S. - Check out the Schmap widget on the right-hand side of the page...
I haven't written this article myself, but came across it on net. Its an amazing must read.........
Funny but true:
Once there was a little island country. The land of this country was thetiny island itself. The total money in circulation was 2 dollars as therewere only two pieces of 1 dollar coins circulating around.
1) There were 3 citizens living on this island country. A owned the land.B and C each owned 1 dollar.
2) B decided to purchase the land from A for 1 dollar. So, now A and C own1 dollar each while B owned a piece of land that is worth 1 dollar.
* The net asset of the country now = 3 dollars.
3) Now C thought that since there is only one piece of land in the country,and land is non producible asset, its value must definitely go up. So, heborrowed 1 dollar from A, and together with his own 1 dollar, he bought theland from B for 2 dollars.
*A has a loan to C of 1 dollar, so his net asset is 1 dollar.* B sold his land and got 2 dollars, so his net asset is 2 dollars.* C owned the piece of land worth 2 dollars but with his 1 dollar debt toA, his net residual asset is 1 dollar.* Thus, the net asset of the country = 4 dollars.
4) A saw that the land he once owned has risen in value. He regrettedhaving sold it. Luckily, he has a 1 dollar loan to C. He then borrowed 2dollars from B and acquired the land back from C for 3 dollars. The paymentis by 2 dollars cash (which he borrowed) and cancellation of the 1 dollarloan to C. As a result, A now owned a piece of land that is worth 3dollars. But since he owed B 2 dollars, his net asset is 1 dollar.
* B loaned 2 dollars to A. So his net asset is 2 dollars.* C now has the 2 coins. His net asset is also 2 dollars.* The net asset of the country = 5 dollars. A bubble is building up.
(5) B saw that the value of land kept rising. He also wanted to own theland. So he bought the land from A for 4 dollars. The payment is byborrowing 2 dollars from C, and cancellation of his 2 dollars loan to A.
* As a result, A has got his debt cleared and he got the 2 coins. His netasset is 2 dollars.* B owned a piece of land that is worth 4 dollars, but since he has a debtof 2 dollars with C, his net Asset is 2 dollars.* C loaned 2 dollars to B, so his net asset is 2 dollars.
* The net asset of the country = 6 dollars; even though, the country hasonly one piece of land and 2 Dollars in circulation.
(6) Everybody has made money and everybody felt happy and prosperous.
(7) One day an evil wind blew, and an evil thought came to C's mind. "Hey,what if the land price stop going up, how could B repay my loan. There isonly 2 dollars in circulation, and, I think after all the land that B ownsis worth at most only 1 dollar, and no more."
(8) A also thought the same way.
(9) Nobody wanted to buy land anymore.
* So, in the end, A owns the 2 dollar coins, his net asset is 2 dollars.* B owed C 2 dollars and the land he owned which he thought worth 4 dollarsis now 1 dollar. So his net asset is only 1 dollar.* C has a loan of 2 dollars to B. But it is a bad debt. Although his netasset is still 2 dollars, his Heart is palpitating.* The net asset of the country = 3 dollars again.
(10) So, who has stolen the 3 dollars from the country ? Of course, beforethe bubble burst B thought his land was worth 4 dollars. Actually, rightbefore the collapse, the net asset of the country was 6 dollars on paper.B's net asset is still 2 dollars, his heart is palpitating.
(11) B had no choice but to declare bankruptcy. C as to relinquish his 2dollars bad debt to B, but in return he acquired the land which is worth 1dollar now.
* A owns the 2 coins, his net asset is 2 dollars.* B is bankrupt, his net asset is 0 dollar. ( he lost everything )* C got no choice but end up with a land worth only 1 dollar
* The net asset of the country = 3 dollars !!!?
Greasemonkey is an extension for firefox which allows users to write custom scripts that can change the UI and behavior of web-pages. I tried writing a small script that would change the layout of an RSS-feed aggregator site->www.waywework.it. Here is a screen shot of how it looks after applying the custom Greasemonkey script. And here is the [...]
December, 23rd, 2008 will be remembered as the most important day in Ruby on Rails history. Merb is, a framework, dedicated for developers. It gives power to build and choose your own tools. On the other hand Rails community is rich with great features in the kitty and well prepared for Rails-2.3 launch in January, 2009 with new features(Rails Metal, application generators, etc).
But, both the sides of fence share a common goal: help developers to write better performing application, easily. That's where Rails 3 + Merb 2 comes in :)
You know those nights when you just have an awesome time? Where the wine is unbelievably good and flows freely, where the food is simple but tasty and leaves you free to talk instead of just ooh and aah over it, where the company is comfortable and warm?
Saturday night was one of those nights for me.
My friends Caroline, Brian and Ellie came over for dinner, and we spent a most excellent evening together. Ellie's just started med school in Chicago, Caroline and Brian have just gotten engaged, and I'm starting a new role at work in January, so we had oodles to celebrate.
Accordingly, we drank a bottle of Schramsberg and a bottle of Ayala (an unbelievably good 1999 brut millésimé from my champagne soulmate, Louisa), some delicious homemade eggnog (courtesy of Brian, and accompanied by fried chickpeas with cilantro), and a gorgeous bottle of Hall Cabernet Sauvignon (which Caroline, far more discerning than I, pronounced too young, but I thoroughly enjoyed).The food was all comfort, all the time: roasted tomato soup, gougères, macaroni and cheese with bacon and mushrooms, and sticky toffee puddings. The music was mainly Christmas songs, with a bit of Arcade Fire thrown in for spice. Ellie insisted on washing some dishes, and I actually let her, which means I must have been tipsier than I realized.
All in all, a fantastic evening.
Roasted Tomato Soup
Adapted from Ina Garten
3 lbs. plum tomatoes, sliced in half lengthwise
1/4 cup plus 2 tbs. olive oil, divided
1 tbs. kosher salt
1 1/2 tsp. ground black pepper
2 tbs. unsalted butter
2 small yellow onions, chopped
6 garlic cloves, minced
1/4 tsp. crushed red pepper flakes
28 oz. canned plum tomatoes, with juice
4 cups fresh basil leaves
1 tsp. fresh thyme leaves, plus extra for garnish
1 quart chicken stock
Pre-heat the oven to 400 degrees Fahrenheit. Toss the fresh tomatoes with 1/4 cup of the olive oil, the salt and the pepper, and spread evenly, in layer, on one large or two small rimmed baking sheets. Roast for 35-45 minutes, until well-browned.
In a large dutch oven or stockpot, heat the remaining 2 tbs. of olive oil and the butter over medium heat. Once the butter has melted, add the onions, garlic and red pepper flakes, and saute until the onions have begun to turn brown around the edges.
Add the canned tomatoes, basil, thyme and chicken stock. Add the roasted tomatoes, with their juices, and bring the mixture to a simmer. Simmer, uncovered, for forty minutes.
Using a stick blender (or a traditional blender, working in batches), blend to desired consistency (I like it thick but not chunky). Taste and adjust for seasoning and serve each bowl sprinkled with a few fresh thyme leaves.
Serves six, generously. Can be made up to a day ahead and reheated gently on the stovetop.
Cucumber is one of the cornerstone for BDD in Ruby on Rails. Non technical or business participants can write tests (called as features or stories) in plain English and cucumber allows the developers to execute these tests.
I was very excited when I started looking at it. In my test.feature file, I have a feature:
Feature: Grant and revoke access
To have proper security model
User should have proper access
Scenario: Revoke access of a user who do not deserves access
Given Gourav is a user who do not deserves access
When system run cron job to verify access
Then Gourav's access should be revoked
There are two ways to execute this file:
1. Cucumber as a plugin (by rake task):
It starts eating 'a' and 'A' characters on windows:
Feture: Grnt nd revoke ccess # fetures/grnt_nd_revoke_ccess.feture
To hve proper security model
User should hve proper ccess
Scenrio: Revoke ccess of user who do not deserves ccess # fetures/test.feture:5
Given Gourv is user who do not deserves ccess # fetures/test.feture:6
When system run cron job to verify ccess # fetures/test.feture:9
Then Gourv's ccess should be revoked # fetures/test.feture:10
cucumber features\test.feature It gives: Feature: Grant and revoke access # features/test.feature
To have proper security model
User should have proper access
Scenario: Revoke access of a user who do not deserves accessC:/ruby/lib/ruby/gems/1.8/
gems/cucumber-0.1.12/bin/../lib/cucumber/tree/scenario.rb:70:in length': undefined methodjlength' for Scenario ng (NoMethodError)
from C:/ruby/lib/ruby/gems/1.8/gems/cucumber-0.1.12/bin/../lib/cucumber/tree/scenario.rb:74:in `max_line_length'
from C:/ruby/lib/ruby/gems/1.8/gems/cucumber-0.1.12/bin/../lib/cucumber/tree/scenario.rb:78:in `padding_length'
from C:/ruby/lib/ruby/gems/1.8/gems/cucumber-0.1.12/bin/../lib/cucumber/formatters/pretty_formatter.rb:197:in `padding_spaces'
from C:/ruby/lib/ruby/gems/1.8/gems/cucumber-0.1.12/bin/../lib/cucumber/formatters/pretty_formatter.rb:58:in `scenario_executing'
from C:/ruby/lib/ruby/gems/1.8/gems/cucumber-0.1.12/bin/../lib/cucumber/broadcaster.rb:15:in `__send__'
from C:/ruby/lib/ruby/gems/1.8/gems/cucumber-0.1.12/bin/../lib/cucumber/broadcaster.rb:15:in `method_missing'
from C:/ruby/lib/ruby/gems/1.8/gems/cucumber-0.1.12/bin/../lib/cucumber/broadcaster.rb:13:in `each'
from C:/ruby/lib/ruby/gems/1.8/gems/cucumber-0.1.12/bin/../lib/cucumber/broadcaster.rb:13:in `method_missing'
... 12 levels...
from C:/ruby/lib/ruby/gems/1.8/gems/cucumber-0.1.12/bin/../lib/cucumber/cli.rb:11:in `execute'
from C:/ruby/lib/ruby/gems/1.8/gems/cucumber-0.1.12/bin/cucumber:6
from C:/ruby/bin/cucumber:19:in `load'
from C:/ruby/bin/cucumber:19
I found a workaround for this:
In file C:\ruby\lib\ruby\gems\1.8\gems\cucumber-0.1.12\lib\cucumber\tree\Scenario.rb on line number 69, I have changed jlength to length:
@length ||= Cucumber.language['scenario'].length + 2 + (@name.nil? ? 0 : @name.length)
Similarly, in file C:\ruby\lib\ruby\gems\1.8\gems\cucumber-0.1.12\lib\cucumber\tree\step.rb on line number 22 I have changed jlength to length:
keyword.length + 1 + name.length
But for a better solution I am still working on issue #81
Last time I wrote my first PHPUnit test for Drupal. Now I’d like to continue writing my new Drupal module using Test Driven Development (TDD). Here are links to the finished test code and production code files that I’ll write below: TddTests.php and tdd.module, in case you want to download and try these tests yourself.
With TDD the first step is always to write a failing unit test. But, which test to write first? What should I try to test? Since the demonstration module I’m writing will display a series of nodes on the screen containing a certain word in their title, let’s try to test that behavior directly, right away:
public function test_search_for_titles()
{
$query = 'FindMe';
$titles = tdd_search_for_titles($query);
}
This doesn’t actually test anything, but it calls a function that will return the titles. Now if we run this test, it will obviously fail since “tdd_search_for_titles” is not defined. Let’s write that. Another rule of TDD is to write just enough production code to get the failing unit test to pass. The simplest way to get this test to pass is to just return a hard coded title like this:
function tdd_search_for_titles($query) {
return array('Hard coded title with the word FindMe');
}
Now the test above passes, along with the first test I wrote in my last post:
$ phpunit TddTests modules/tdd/TddTests.php PHPUnit 3.2.21 by Sebastian Bergmann. .. Time: 0 seconds OK (2 tests)
This seems silly, but at least we’ve started writing and executing code in our new module. Since our test is not actually testing anything, let’s add some real test code to it by checking that the titles returned actually contain the query string:
public function test_search_for_titles()
{
$query = 'FindMe';
$titles = tdd_search_for_titles($query);
foreach ($titles as $title) {
$this->assertTrue(stripos($title, $query) > 0);
}
}
Run the test again: it still passes. It’s important when using TDD to continuously run your tests as you write code, as often as every 30 seconds or 1-2 minutes. That way as soon as your tests fail, you know immediately what caused the problem: whatever code you changed during the past 1-2 minutes. One benefit of TDD is that you rarely need to use a debugger to figure out what is wrong since you execute and test your changes so frequently. Next, as a sanity check, let’s try breaking the code on purpose and checking if our test is really working or not:
function tdd_search_for_titles($query) {
return array('Hard coded title with the word FindXYZMe');
}
Now the test fails (good!):
$ phpunit TddTests modules/tdd/TddTests.php PHPUnit 3.2.21 by Sebastian Bergmann. .F Time: 0 seconds There was 1 failure: 1) test_search_for_titles(TddTests) Failed asserting that <boolean:false> is true. /Users/pat/htdocs/drupal3/modules/tdd/TddTests.php:15 FAILURES! Tests: 2, Failures: 1.
I frequently do something like this when some new tests pass for the first time, just to be sure they are really executing and calling the code I intended them to. Sometimes trusting your tests too much can be dangerous! If we remove the “XYZ” change the test will pass again. Once the test is passing again we can move on.
So far we haven’t done much. All we have done is to prove that we can write a function to return a hard-coded string. Let’s continue by testing that we can query actual data in the MySQL database. But first we need to create some data for our function to look for. The simplest way to do that would be to open Drupal in a web browser and to create some nodes (web pages) that have the query string in their title. This is a good way to get started, but can lead to trouble down the road since our test will now rely on someone having created these pages manually. If we run the tests using a different database or on someone else’s machine they will fail. A better approach would be to have the test itself create the data it expects. Here’s how to do that:
public function test_search_for_titles()
{
$login_form = array('name' => 'admin', 'pass' => 'adminpassword');
user_authenticate($login_form);
$node = new stdClass();
$node->title = 'This title contains the word FindSomethingElse';
$node->body = 'This is the body of the node';
node_save($node);
$query = 'FindSomethingElse';
$titles = tdd_search_for_titles($query);
foreach ($titles as $title) {
$this->assertTrue(stripos($title, $query) > 0);
}
node_delete($node->nid);
}
There are a few different things going on here:
If we run the test it will fail, of course, since our hard coded title does not contain “FindSomethingElse.” Instead of changing the hard coded string to make the test pass, let’s actually do some real coding and write a SQL statement to get the titles from MySQL:
function tdd_search_for_titles($query) {
$titles = array();
$result = db_query("SELECT title FROM {node}");
while ($node = db_fetch_object($result)) {
$titles[] = $node->title;
}
return $titles;
}
Finally we have started writing code that our module will actually use. Here we take the query string, construct a select statement and execute it using db_query(). Once we have the results, we return the titles as an array of strings. Let’s run our test and see what happens:
$ phpunit TddTests modules/tdd/TddTests.php Failed asserting that <boolean:false> is true.
Oops — it failed! What went wrong? If we add another check to the test, we can get some more information:
$this->assertEquals(count($titles), 1);
And if we run again:
$ phpunit TddTests modules/tdd/TddTests.php Failed asserting that <integer:1> matches expected value <integer:27>.
The problem is that there are 27 titles returned, instead of just the one we created. If we take a second look at the SQL statement we see right away that there is no WHERE clause, causing all of the titles in the database to be returned. Now, if we fix the SQL statement, the test should pass:
$result = db_query(
"SELECT title FROM {node} WHERE title LIKE '%%%s%%'", $query);
Let’s see:
phpunit TddTests modules/tdd/TddTests.php Failed asserting that <integer:1> matches expected value <integer:3>
Now what’s the problem?? It turns out that both the test and production functions are working properly, and there really are 3 nodes in the database with “FindSomethingElse” in the title. The reason why is that our call to node_delete() was not executed when we ran our test above and it failed (twice). We’ll fix that below. For now, let’s just clean up the database and remove the extra test records. The best thing to do is just to delete them from the Drupal admin console (Administer->Content management->Content). Now when you are sure no other nodes exist with “FindSomethingElse” in the title run the test again and it will pass.
Now… why were extra test node records created when the test failed? What happened is that the call to node_delete() is never executed if any of the asserts fail. That is, the test execution stops as soon as there is a failing assert statement. I suppose this makes sense; we know the overall test will fail if there is even one failing assert statement, and so there’s no need to continue executing the test code.
The solution is to refactor the test code and use two new functions called setup() and teardown(), as follows:
public function setup()
{
$login_form = array('name' => 'admin', 'pass' => 'adminpassword');
user_authenticate($login_form);
$this->node = new stdClass();
$this->node->title = 'This title contains the word FindSomethingElse';
$this->node->body = 'This is the body of the node';
node_save($this->node);
}
public function teardown()
{
node_delete($this->node->nid);
}
The way this works is that setup() is called once for each test in the test class, just before the test function is called. In our case it will be called twice: once for test_tdd_help() and once for test_search_for_titles(). This gives us a chance to create test data and perform other setup tasks before each test is executed. As you might guess, teardown() is called once for each test also, right after the test function finishes. This gives us a chances to remove our test data, even if the test fails. One thing to note about this: you have to save the $node object inside the test class so that it can be accessed from teardown() and from the tests themselves if necessary; so “$node” becomes “$this->node”.
Here are the links again to the final test and code files: TddTests.php and tdd.module. In my next post I’ll finish up the code for tdd.module and TddTests.php without providing as much detail, and then move quickly to a discussion of how this code can be integrated with Drupal and included in a working web site.
Last time I wrote my first PHPUnit test for Drupal. Now I’d like to continue writing my new Drupal module using Test Driven Development (TDD). Here are links to the finished test code and production code files that I’ll write below: TddTests.php and tdd.module, in case you want to download and try these tests yourself.
With TDD the first step is always to write a failing unit test. But, which test to write first? What should I try to test? Since the demonstration module I’m writing will display a series of nodes on the screen containing a certain word in their title, let’s try to test that behavior directly, right away:
public function test_search_for_titles()
{
$query = 'FindMe';
$titles = tdd_search_for_titles($query);
}
This doesn’t actually test anything, but it calls a function that will return the titles. Now if we run this test, it will obviously fail since “tdd_search_for_titles” is not defined. Let’s write that. Another rule of TDD is to write just enough production code to get the failing unit test to pass. The simplest way to get this test to pass is to just return a hard coded title like this:
function tdd_search_for_titles($query) {
return array('Hard coded title with the word FindMe');
}
Now the test above passes, along with the first test I wrote in my last post:
$ phpunit TddTests modules/tdd/TddTests.php PHPUnit 3.2.21 by Sebastian Bergmann. .. Time: 0 seconds OK (2 tests)
This seems silly, but at least we’ve started writing and executing code in our new module. Since our test is not actually testing anything, let’s add some real test code to it by checking that the titles returned actually contain the query string:
public function test_search_for_titles()
{
$query = 'FindMe';
$titles = tdd_search_for_titles($query);
foreach ($titles as $title) {
$this->assertTrue(stripos($title, $query) > 0);
}
}
Run the test again: it still passes. It’s important when using TDD to continuously run your tests as you write code, as often as every 30 seconds or 1-2 minutes. That way as soon as your tests fail, you know immediately what caused the problem: whatever code you changed during the past 1-2 minutes. One benefit of TDD is that you rarely need to use a debugger to figure out what is wrong since you execute and test your changes so frequently. Next, as a sanity check, let’s try breaking the code on purpose and checking if our test is really working or not:
function tdd_search_for_titles($query) {
return array('Hard coded title with the word FindXYZMe');
}
Now the test fails (good!):
$ phpunit TddTests modules/tdd/TddTests.php PHPUnit 3.2.21 by Sebastian Bergmann. .F Time: 0 seconds There was 1 failure: 1) test_search_for_titles(TddTests) Failed asserting that <boolean:false> is true. /Users/pat/htdocs/drupal3/modules/tdd/TddTests.php:15 FAILURES! Tests: 2, Failures: 1.
I frequently do something like this when some new tests pass for the first time, just to be sure they are really executing and calling the code I intended them to. Sometimes trusting your tests too much can be dangerous! If we remove the “XYZ” change the test will pass again. Once the test is passing again we can move on.
So far we haven’t done much. All we have done is to prove that we can write a function to return a hard-coded string. Let’s continue by testing that we can query actual data in the MySQL database. But first we need to create some data for our function to look for. The simplest way to do that would be to open Drupal in a web browser and to create some nodes (web pages) that have the query string in their title. This is a good way to get started, but can lead to trouble down the road since our test will now rely on someone having created these pages manually. If we run the tests using a different database or on someone else’s machine they will fail. A better approach would be to have the test itself create the data it expects. Here’s how to do that:
public function test_search_for_titles()
{
$login_form = array('name' => 'admin', 'pass' => 'adminpassword');
user_authenticate($login_form);
$node = new stdClass();
$node->title = 'This title contains the word FindSomethingElse';
$node->body = 'This is the body of the node';
node_save($node);
$query = 'FindSomethingElse';
$titles = tdd_search_for_titles($query);
foreach ($titles as $title) {
$this->assertTrue(stripos($title, $query) > 0);
}
node_delete($node->nid);
}
There are a few different things going on here:
If we run the test it will fail, of course, since our hard coded title does not contain “FindSomethingElse.” Instead of changing the hard coded string to make the test pass, let’s actually do some real coding and write a SQL statement to get the titles from MySQL:
function tdd_search_for_titles($query) {
$titles = array();
$result = db_query("SELECT title FROM {node}");
while ($node = db_fetch_object($result)) {
$titles[] = $node->title;
}
return $titles;
}
Finally we have started writing code that our module will actually use. Here we take the query string, construct a select statement and execute it using db_query(). Once we have the results, we return the titles as an array of strings. Let’s run our test and see what happens:
$ phpunit TddTests modules/tdd/TddTests.php Failed asserting that <boolean:false> is true.
Oops — it failed! What went wrong? If we add another check to the test, we can get some more information:
$this->assertEquals(count($titles), 1);
And if we run again:
$ phpunit TddTests modules/tdd/TddTests.php Failed asserting that <integer:1> matches expected value <integer:27>.
The problem is that there are 27 titles returned, instead of just the one we created. If we take a second look at the SQL statement we see right away that there is no WHERE clause, causing all of the titles in the database to be returned. Now, if we fix the SQL statement, the test should pass:
$result = db_query(
"SELECT title FROM {node} WHERE title LIKE '%%%s%%'", $query);
Let’s see:
phpunit TddTests modules/tdd/TddTests.php Failed asserting that <integer:1> matches expected value <integer:3>
Now what’s the problem?? It turns out that both the test and production functions are working properly, and there really are 3 nodes in the database with “FindSomethingElse” in the title. The reason why is that our call to node_delete() was not executed when we ran our test above and it failed (twice). We’ll fix that below. For now, let’s just clean up the database and remove the extra test records. The best thing to do is just to delete them from the Drupal admin console (Administer->Content management->Content). Now when you are sure no other nodes exist with “FindSomethingElse” in the title run the test again and it will pass.
Now… why were extra test node records created when the test failed? What happened is that the call to node_delete() is never executed if any of the asserts fail. That is, the test execution stops as soon as there is a failing assert statement. I suppose this makes sense; we know the overall test will fail if there is even one failing assert statement, and so there’s no need to continue executing the test code.
The solution is to refactor the test code and use two new functions called setup() and teardown(), as follows:
public function setup()
{
$login_form = array('name' => 'admin', 'pass' => 'adminpassword');
user_authenticate($login_form);
$this->node = new stdClass();
$this->node->title = 'This title contains the word FindSomethingElse';
$this->node->body = 'This is the body of the node';
node_save($this->node);
}
public function teardown()
{
node_delete($this->node->nid);
}
The way this works is that setup() is called once for each test in the test class, just before the test function is called. In our case it will be called twice: once for test_tdd_help() and once for test_search_for_titles(). This gives us a chance to create test data and perform other setup tasks before each test is executed. As you might guess, teardown() is called once for each test also, right after the test function finishes. This gives us a chances to remove our test data, even if the test fails. One thing to note about this: you have to save the $node object inside the test class so that it can be accessed from teardown() and from the tests themselves if necessary; so “$node” becomes “$this->node”.
Here are the links again to the final test and code files: TddTests.php and tdd.module. In my next post I’ll finish up the code for tdd.module and TddTests.php without providing as much detail, and then move quickly to a discussion of how this code can be integrated with Drupal and included in a working web site.
MooTools is a compact, modular, Object-Oriented JavaScript framework designed for the intermediate to advanced JavaScript developer. It allows you to write powerful, flexible, and cross-browser code with its elegant, well documented, and coherent API. http://mootools.net/
Check out the site who has used mootools --> http://mochaui.com/demo/
I spent Thanksgiving at my mom's new home in St. Augustine, Florida this year. She just moved in two months ago, and so she's in the box phase - you open one, you see what you see, and what you see is probably not what you need.
Case in point? The rolling pin was nowhere to be found, so I rolled out the pastry dough for my apple pie with a bottle of Pinot Noir, something I do not recommend to anyone but which certainly made for an interesting image (and a slightly lumpy crust). Of course, we didn't really care so much, since the pie in question turned out to be so darned ambrosial.
Typically, our family goes in for a very traditional apple pie: not too deep of dish, apples sliced fairly thin, filling flavored with cinnamon, sugar, nutmeg, and perhaps a bit of lemon juice. This time around, I decided to splash out and make Ina Garten's deep dish apple pie. She keeps her apple slices thick and hearty - they're more chunks, really - and uses a whopping four pounds of them. She adds orange and lemon juice and zest, and rounds out the spices with a bit of allspice.I am not exaggerating when I tell you that this is the best apple pie I've ever tasted. It is complex, but not overwhelming, and not even a little boring. Plus, that final egg wash makes things awful pretty, no?
Room service for breakfast is one of life's great pleasures. There's something unmistakably luxurious about taking breakfast in your room - you're cocooned a bit longer in your sanctuary, able to read your paper without looking rude, able to ease into your morning peacefully rather than surrounding by the jangle and clatter of cutlery.
When I travel for business, those extra twenty minutes of peace are precious to me, and I eat breakfast in my room whenever I can get away with it. My trip to Mumbai was no exception, and the ITC Maratha performed admirably. Their basket of toast and pastry arrived warm and crusty each morning, and though Indian jams tend to run too sweet for my taste, the butter was fantastic.
And, most important - the coffee was strong, rich and hot, fortifying me for long days of meetings and hand-shaking.
Software development and integration methods continue to evolve...
Last week Matt discussed how he's thinking about Digital Strategy integration and we discussed ROA (Resource Oriented Architecture) and WOA (Web Oriented Architecture). They are specialized types of SOA (Service Oriented Architecture). Neal called them " SOA that actually works".
If you haven't heard about it or used it yet, go understand it, learn it, use it. Gerardo, Matt and other engineers can help...
Enjoying my new white iphone; looking for some good games, any ideas?
Been playing Brain Tuner..
can you beat my best time?
In my previous post I wrote some typical Drupal code that lists nodes containing a given word in their title. In some upcoming posts I’ll rewrite that module using Test Driven Development and see whether the module turns out differently. But to get started with TDD in Drupal we need to be able to write our first test. Before we can even do that we need to install PHPUnit. Once you have PHPUnit installed, test that it is working properly by writing a trivial test file called FirstTest.php somewhere on your hard drive:
<?php
class FirstTest extends PHPUnit_Framework_TestCase
{
public function test_two_plus_two_is_four()
{
$this->assertEquals(2+2, 4);
}
}
?>
To run this you should use PHPUnit as follows from the same folder containing the test file:
$ phpunit FirstTest PHPUnit 3.2.21 by Sebastian Bergmann. . Time: 0 seconds OK (1 test)
If you get errors running this command then review the install instructions for PHPUnit and install it again if necessary.
Next: how do we apply PHPUnit to Drupal? Let’s create a new module folder, and an “info” file in it so Drupal knows what our new module is called: “tdd” for example. So we create a folder drupal/modules/tdd and a tdd.info file in this folder that contains:
name = tdd description = Module written with TDD package = TDD Demo Modules version = VERSION core = 6.x
Now let’s write our first test. PHPUnit convention is that if you have a PHP class called XYZ, you would write a test class for it called XYZTests, and by default place the test class in XYZTests.php in the same folder. Since we’re using Drupal and don’t have any object oriented code, let’s just call our test class TddTests, named after our new module, and put it into a new file in the same folder called TddTests.php. It needs to be a subclass of PHPUnit_Framework_TestCase like this:
<?php
class TddTests extends PHPUnit_Framework_TestCase
{
public function test_tdd_help()
{
$this->assertEquals(
tdd_help('admin/content/tdd'), "<p>Help for TDD module.</p>");
}
}
?>
This test will call our new module’s “help” function with the path to the module’s new page and make sure we get the proper help. This seems like a good first test to write, since it’s very simple but still proves that we can call Drupal code from PHPUnit. Let’s run it and see what happens… But first you have to cd to the folder containing the TddTests.php file. PHPUnit also assumes that by default you’re executing the tests from the folder containing the test file. Let's try running our test:
$ cd modules/tdd $ phpunit TddTests.php PHPUnit 3.2.21 by Sebastian Bergmann. Fatal error: Call to undefined function tdd_help() in /Users/pat/htdocs/drupal/modules/tdd/TddTests.php on line 6
Obviously the test fails because we haven’t written our new module yet. This seems like a waste of time, but we've taken the first step in the TDD cycle: write a failing unit test. Now let's start writing tdd.module:
<?php
function tdd_help($path, $arg) {
switch ($path) {
case 'admin/content/tdd':
return '<p>Help for TDD module.</p>';
}
}
?>
If you run this test again you’ll get the same error message. So now what’s wrong? We forgot to enable our new module in the Drupal admin console, so the “tdd_help” function is still undefined. To enable it, open the Drupal admin console, go to the Administer->Site Building->Modules page and look at the bottom for a section called “TDD Demo Modules.”
But now if you run PHPUnit you’ll still get the same error. The mostly empty “TDD” module is now setup and working inside of Drupal, but PHPUnit has no idea what Drupal is or how to load it. What we need to do is declare somehow in TddTests.php to include and initialize the Drupal framework. To get this to work, we can use the index.php file in the root folder of Drupal app as an example. This is the PHP file that handles all incoming requests to Drupal by initializing the framework, executing the proper menu callback and displaying the result. If you look at the top of the index.php file, you’ll see these two lines:
require_once './includes/bootstrap.inc'; drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
If we copy these 2 lines into TddTests.php like this:
<?php
require_once './includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
class TddTests extends PHPUnit_Framework_TestCase
{
public function test_tdd_help()
{
$this->assertEquals(
tdd_help('admin/content/tdd'), "<p>Help for TDD module.</p>");
}
}
?>
… and try to run our test again we get:
$ phpunit TddTests Warning: require_once(./includes/bootstrap.inc): failed to open stream: No such file or directory in /Users/pat/htdocs/drupal/modules/tdd/TddTests.php on line 2 Fatal error: require_once(): Failed opening required './includes/bootstrap.inc' (include_path='.:/usr/local/php5x/lib/php') in /Users/pat/htdocs/drupal/modules/tdd/TddTests.php on line 2
The problem now is that the include line has the wrong relative path. If you corrected it you would still get more errors trying to include other Drupal files. It turns out that the drupal_bootstrap function assumes that the current directory is set to the root folder of your app: the location of index.php. To get it all to work, we just need to execute the tests from the root folder:
$ cd ~/htdocs/drupal $ phpunit modules/tdd/TddTests.php PHPUnit 3.2.21 by Sebastian Bergmann. Class modules/tdd/TddTests could not be found in modules/tdd/TddTests.php.
This last error appears because we aren’t using the PHPUnit command line properly. The first parameter is the test class name; the second optional parameter that we need to use now is the test file path. Here’s the proper command line to use:
$ phpunit TddTests modules/tdd/TddTests.php PHPUnit 3.2.21 by Sebastian Bergmann. . Time: 0 seconds OK (1 test)
Finally we’ve successfully written and executed our first test! More than that, this is our first step toward writing a Drupal module using TDD. Next time I’ll get to work writing the rest of the module using TDD with these files as a starting point.
In my previous post I wrote some typical Drupal code that lists nodes containing a given word in their title. In some upcoming posts I’ll rewrite that module using Test Driven Development and see whether the module turns out differently. But to get started with TDD in Drupal we need to be able to write our first test. Before we can even do that we need to install PHPUnit. Once you have PHPUnit installed, test that it is working properly by writing a trivial test file called FirstTest.php somewhere on your hard drive:
<?php
class FirstTest extends PHPUnit_Framework_TestCase
{
public function test_two_plus_two_is_four()
{
$this->assertEquals(2+2, 4);
}
}
?>
To run this you should use PHPUnit as follows from the same folder containing the test file:
$ phpunit FirstTest PHPUnit 3.2.21 by Sebastian Bergmann. . Time: 0 seconds OK (1 test)
If you get errors running this command then review the install instructions for PHPUnit and install it again if necessary.
Next: how do we apply PHPUnit to Drupal? Let’s create a new module folder, and an “info” file in it so Drupal knows what our new module is called: “tdd” for example. So we create a folder drupal/modules/tdd and a tdd.info file in this folder that contains:
name = tdd description = Module written with TDD package = TDD Demo Modules version = VERSION core = 6.x
Now let’s write our first test. PHPUnit convention is that if you have a PHP class called XYZ, you would write a test class for it called XYZTests, and by default place the test class in XYZTests.php in the same folder. Since we’re using Drupal and don’t have any object oriented code, let’s just call our test class TddTests, named after our new module, and put it into a new file in the same folder called TddTests.php. It needs to be a subclass of PHPUnit_Framework_TestCase like this:
<?php
class TddTests extends PHPUnit_Framework_TestCase
{
public function test_tdd_help()
{
$this->assertEquals(
tdd_help('admin/content/tdd'), "<p>Help for TDD module.</p>");
}
}
?>
This test will call our new module’s “help” function with the path to the module’s new page and make sure we get the proper help. This seems like a good first test to write, since it’s very simple but still proves that we can call Drupal code from PHPUnit. Let’s run it and see what happens… But first you have to cd to the folder containing the TddTests.php file. PHPUnit also assumes that by default you’re executing the tests from the folder containing the test file. Let's try running our test:
$ cd modules/tdd $ phpunit TddTests.php PHPUnit 3.2.21 by Sebastian Bergmann. Fatal error: Call to undefined function tdd_help() in /Users/pat/htdocs/drupal/modules/tdd/TddTests.php on line 6
Obviously the test fails because we haven’t written our new module yet. This seems like a waste of time, but we've taken the first step in the TDD cycle: write a failing unit test. Now let's start writing tdd.module:
<?php
function tdd_help($path, $arg) {
switch ($path) {
case 'admin/content/tdd':
return '<p>Help for TDD module.</p>';
}
}
?>
If you run this test again you’ll get the same error message. So now what’s wrong? We forgot to enable our new module in the Drupal admin console, so the “tdd_help” function is still undefined. To enable it, open the Drupal admin console, go to the Administer->Site Building->Modules page and look at the bottom for a section called “TDD Demo Modules.”
But now if you run PHPUnit you’ll still get the same error. The mostly empty “TDD” module is now setup and working inside of Drupal, but PHPUnit has no idea what Drupal is or how to load it. What we need to do is declare somehow in TddTests.php to include and initialize the Drupal framework. To get this to work, we can use the index.php file in the root folder of Drupal app as an example. This is the PHP file that handles all incoming requests to Drupal by initializing the framework, executing the proper menu callback and displaying the result. If you look at the top of the index.php file, you’ll see these two lines:
require_once './includes/bootstrap.inc'; drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
If we copy these 2 lines into TddTests.php like this:
<?php
require_once './includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
class TddTests extends PHPUnit_Framework_TestCase
{
public function test_tdd_help()
{
$this->assertEquals(
tdd_help('admin/content/tdd'), "<p>Help for TDD module.</p>");
}
}
?>
… and try to run our test again we get:
$ phpunit TddTests Warning: require_once(./includes/bootstrap.inc): failed to open stream: No such file or directory in /Users/pat/htdocs/drupal/modules/tdd/TddTests.php on line 2 Fatal error: require_once(): Failed opening required './includes/bootstrap.inc' (include_path='.:/usr/local/php5x/lib/php') in /Users/pat/htdocs/drupal/modules/tdd/TddTests.php on line 2
The problem now is that the include line has the wrong relative path. If you corrected it you would still get more errors trying to include other Drupal files. It turns out that the drupal_bootstrap function assumes that the current directory is set to the root folder of your app: the location of index.php. To get it all to work, we just need to execute the tests from the root folder:
$ cd ~/htdocs/drupal $ phpunit modules/tdd/TddTests.php PHPUnit 3.2.21 by Sebastian Bergmann. Class modules/tdd/TddTests could not be found in modules/tdd/TddTests.php.
This last error appears because we aren’t using the PHPUnit command line properly. The first parameter is the test class name; the second optional parameter that we need to use now is the test file path. Here’s the proper command line to use:
$ phpunit TddTests modules/tdd/TddTests.php PHPUnit 3.2.21 by Sebastian Bergmann. . Time: 0 seconds OK (1 test)
Finally we’ve successfully written and executed our first test! More than that, this is our first step toward writing a Drupal module using TDD. Next time I’ll get to work writing the rest of the module using TDD with these files as a starting point.
Things that went well
- We grew to become a high performing team. Everyone has improved and, in their own ways, led the CoE... from Jaspreet creating design patterns to Ketan introducing peer architect concept.
- We developed hands-on skills. No more chickens... we're proud to be pigs on projects.
- We leveraged each other. Never hesitated in asking for help, always ready to help and have unquestionable trust in each other.
- We learned new skills and domains. Ruby, PHP, Finance, Learn.
- We improved sponsor confidence. Senior IT leadership and Firm functions recognize and seek CoE member's advice.
- We increased developer reach. ODC isn't 7000 miles aways anymore. Our engineers are talking to us now.
- We enabled Scrum at our organization. We really did. How else can design decisions be finalized in short sprints of 40 concurrent projects?
- We had fun doing it. Meeting daily never became a drag. Central park outing, Amazing race skits, birthday celebrations are all part of it.
I read an interesting post today by David Chelimsky who wrote RSpec A case against a case against mocking and stubbing. Its about mocking in testing and isolated vs integrated tests. I liked it all but what I particularly liked is how it describes the process of outside-in development.
To quote:
I read an interesting post today by David Chelimsky who wrote RSpec A case against a case against mocking and stubbing. Its about mocking in testing and isolated vs integrated tests. I liked it all but what I particularly liked is how it describes the process of outside-in development.
To quote:

I'm crazy for cricket. When Indian cricket is on, I cannot do anything else. I have to watch the game. And tonight is one of those nights. India and England will play a test match in Chennai (if the rain gods don't go there). Up late tonight to watch it. I predict that India will win by more than 4 wickets or more than 150 runs. Go Sachin! Go Zaheer!
Yes, cricket is my religion. I grew up playing the game and can play/watch all forms of cricket - test, one-day, 20-20 and even street cricket! Some of you have seen a cricket bat in my office. Always up for a quick knock in the lobby...
I'm a decent medium pace bowler and a dependable middle order batsman. My love for pace shaped how I bowl and bat... I don't get to play much cricket in the US but I'll get to play again daily when I go back to India (yes, some day).
The place: Old Greenwich Elementary School. The year: 1986. The occasion: the 100th anniversary of the dedication of the Statue of Liberty.
Yes, that's right - my love affair with France began way back when, back when my mother and her siblings spoke in a mysterious language when they didn't want us to hear, back when I took after-school French classes where I learned stories to help me count to ten (Under Trois, Cat sank; Sis said we'd had enough of this.), back when I wore an oh-so-American kerchief along with my red beret.
That's right, folks. True love starts early - in my case, at the age of six.
...this is what Neal Ford said in a discussion yesterday and it stuck with me. It sounds paradoxical but is totally true for software. We do not observe this anywhere else in the physical world.
That's why we need good engineers to design and write simple software.
Neal Ford puts it much more eloquently and I've requested him to blog about it... I'll post a link when it comes out...
The Open Handset Alliance announced the joining of 14 additional companies yesterday. This includes the likes of Sony Erricson, Toshiba and Vodafone amongst others. New members will either deploy compatible Android devices, contribute significant code to the Android Open Source Project, or support the ecosystem through products and services that will accelerate the availability of Android-based [...]
Here are some of my thoughts on Architecture CoE's performance this year. I'll discuss what went well and what didn't go so well in separate posts.
Things that didn't go well
- We were not fearless. E.g. we failed to take bold steps to achieve great things e.g. we tolerated the pain from our heavyweight survey platform and failed to reject it
- We were not thorough. We released 3-5 applications that needed hotfixes because we broke existing functionality. Yes, its a small % (total 120+ releases) but nothing short of perfection is expected from us in such maintenance releases. We cannot shortchange regression testing (case for automated testing but manual if/while tests aren't fully automated)
- We were not focused. We ended up spreading our attention on many things (Presentation Service, Common Service, Reporting Service, Real-life testing, Improve our design, Automated build/test/deploy/CI of Java apps, Ruby/LAMP). We could not make much progress in half of these areas
We developers need to win this battle. Ofcource I am on the Unit Testing side and thatz the reason for this battle. We want to improve. We want to stop debugging, or we may call it post coding or post delivery nightmare.
We all are. Because thatz what we do most often. The percentage of time we
spend on debugging is far more than coding.
Every programmer knows they should write tests for their code. Few do. The universal response to "Why not?" is "I'm in too much of a hurry." This quickly becomes a vicious cycle- the more pressure you feel, the fewer tests you write. The fewer tests you write, the less productive you are and the less stable you code becomes. The less productive and accurate you are, the more pressure you feel.
Once upon a time, two very lucky young ladies took a trip to Prague. Along the way, they sampled many, many cups of hot chocolate. In the many days since, they've never found anything even close to the perfection that is Czech hot chocolate, particularly not in the surprisingly quality-hot-chocolate-free land of France.
Until now.
On the suggestion of a colleague, I decided to give French hot chocolate another go on this trip, and I was not disappointed. On Sunday afternoon, I paid a visit to Angelina, the legendary teahouse on rue de Rivoli, across from the Jardins des Tuileries. I waited in a long line of tourists and well-heeled locals for a table, and was seated on the second floor, overlooking the entrance.I hunkered down with my book, ordered a chocolat chaud and a carafe d'eau, and awaited the arrival of the chocolate. It arrived in a little pitcher, whipped cream on the side, with a spoon for dolloping at will - or, in my case, for occasionally eating the thick concoction like a stew.
Like the mythical hot chocolates of Prague, this version was rich and deeply flavorful, not at all like the sugary water served in college cafeterias or at skating rinks here in the States. It's basically melted dark chocolate, lightly sweetened and lightened ever so slightly with a touch of milk. In short, it's pure indulgence. And I really liked being to add my cream a bit at a time, so that it didn't melt too much before I got to enjoy it.
So, does Angelina's version live up to that of Prague's Café Louvre or Café Carolina? Not quite. But after years of wandering in the chocolat chaud wilderness, I feel secure in the knowledge that the French have not lost their touch.
To illustrate how to use Test Driven Development while developing a Drupal module, we first need an example module to write: Let's assume we want a page in our Drupal admin console that lists all of nodes in the database, similar to the Administer->Content Management->Content page. It will also filter and sort the list, but instead of filtering on the status and type of nodes, it will filter on words contained in the node title.
The first thing I did was to write the module as quickly as possible as I normally would have without using TDD. Here’s the completed module: demo.module, and demo.info. If you want to follow along this demonstration just install a fresh copy of the latest version of Drupal 6.x (Drupal 6.6 when I wrote this); be sure to create and use a new MySQL database to use in these examples so your existing work won’t get in the way. Then download the demo.module and demo.info files into a new "demo" module folder and enable the demo module in your admin console (Administer->Site building->Modules). It will be called "Demo" and will appear at the bottom of the list of modules in a section called "TDD Demo Modules."
Once the Demo module is enabled, look for a new page in your admin console at Administer->Content management->Demo Page. It will initially display a list of all the nodes in your database, and also allow you to enter a keyword to show only the nodes whose title contains the given word.
If you are using a new empty database, create a couple of pages (Create Content->Page) to have some data to display here. I created two pages with these titles (body text doesn't matter):
A quick review of the module code will reveal nothing special. Demo.module is very simple… it has some boilerplate code (demo_help and demo_menu), and then displays the new “Demo Page” with the demo_page_view function:
function demo_page_view($keys = NULL)
{
$header = array(
array('data' => t('Page Title'), 'field' => 'title', 'sort' => 'asc')
);
$sql = "SELECT * FROM {node} WHERE title LIKE '%%%s%%'";
$sql .= tablesort_sql($header);
$result = pager_query($sql, 50, 0 , NULL, $keys);
$rows = array();
while ($data = db_fetch_object($result)) {
$rows[] = array(check_plain($data->title));
}
if (empty($rows)) {
$rows[] = array(array('data' => 'No pages match the given pattern.'));
}
$output = drupal_get_form('demo_pattern_form', $keys);
$output .= "Pages matching this pattern:";
$output .= theme('table', $header, $rows);
$output .= theme('pager', NULL, 50, 0);
return $output;
}
This is really all there is to demo.module – this function generates a SQL statement using tablesort_sql, then selects all the matching nodes from the database using pager_query and builds an array containing the matches. Then it returns the HTML for a table containing the matching records using the currently selected theme. Later in the module file there are more functions that handle navigation and processing for the “demo_pattern_form.”
This code is so simple it’s hard to imagine that anything could be wrong with it: in just a few lines it gets the data we need and also displays it. It’s also very typical Drupal module code. If you look around the Drupal code base or at code from 3rd party modules you will see a lot of code that looks just like this. It uses common Drupal functions like “theme,” “pager_query” and “db_fetch_object.”
But in my opinion there are a two things wrong with this example module:
A veteran Drupal developer would disagree with the second point, of course: obviously the SQL statement used in demo_page_view really is the only custom behavior here, and all of the other calls to Drupal functions like drupal_get_form, table_sort_sql, etc., are used to help display our data in a Drupal page. But imagine a PHP developer who wasn’t yet very familiar with Drupal looking at this module file for the first time: how would she or he have any idea what this module did? Or where to begin to change it’s behavior if necessary? This is another important use of tests: as living documentation for what code does and how it should work.
This is a silly example, but imagine if this module did something sophisticated and important to your business. Then imagine if your business decided to upgrade from one version of Drupal to another – or imagine that your business decided to use DJango, Joomla or some other CMS system… or to implement it using custom Java or Ruby code. Then the question would become: what part of the module’s code is ours that we want to keep? And what part of the module is simply needed to coexist with Drupal? Right now it’s not so easy to tell.
I believe that using TDD while writing a Drupal module will not only provide the normal benefits of testing: emergent design, living documentation and simply more robust code, but will also lead to isolating your business’s code from the framework in a natural way. By writing unit tests first, you will have to think about what you are trying to test: your business logic only and not the Drupal framework itself.
In my next post we’ll get started by installing PHPUnit and writing our first PHPUnit unit test with Drupal.
To illustrate how to use Test Driven Development while developing a Drupal module, we first need an example module to write: Let's assume we want a page in our Drupal admin console that lists all of nodes in the database, similar to the Administer->Content Management->Content page. It will also filter and sort the list, but instead of filtering on the status and type of nodes, it will filter on words contained in the node title.
The first thing I did was to write the module as quickly as possible as I normally would have without using TDD. Here’s the completed module: demo.module, and demo.info. If you want to follow along this demonstration just install a fresh copy of the latest version of Drupal 6.x (Drupal 6.6 when I wrote this); be sure to create and use a new MySQL database to use in these examples so your existing work won’t get in the way. Then download the demo.module and demo.info files into a new "demo" module folder and enable the demo module in your admin console (Administer->Site building->Modules). It will be called "Demo" and will appear at the bottom of the list of modules in a section called "TDD Demo Modules."
Once the Demo module is enabled, look for a new page in your admin console at Administer->Content management->Demo Page. It will initially display a list of all the nodes in your database, and also allow you to enter a keyword to show only the nodes whose title contains the given word.
If you are using a new empty database, create a couple of pages (Create Content->Page) to have some data to display here. I created two pages with these titles (body text doesn't matter):
A quick review of the module code will reveal nothing special. Demo.module is very simple… it has some boilerplate code (demo_help and demo_menu), and then displays the new “Demo Page” with the demo_page_view function:
function demo_page_view($keys = NULL)
{
$header = array(
array('data' => t('Page Title'), 'field' => 'title', 'sort' => 'asc')
);
$sql = "SELECT * FROM {node} WHERE title LIKE '%%%s%%'";
$sql .= tablesort_sql($header);
$result = pager_query($sql, 50, 0 , NULL, $keys);
$rows = array();
while ($data = db_fetch_object($result)) {
$rows[] = array(check_plain($data->title));
}
if (empty($rows)) {
$rows[] = array(array('data' => 'No pages match the given pattern.'));
}
$output = drupal_get_form('demo_pattern_form', $keys);
$output .= "Pages matching this pattern:";
$output .= theme('table', $header, $rows);
$output .= theme('pager', NULL, 50, 0);
return $output;
}
This is really all there is to demo.module – this function generates a SQL statement using tablesort_sql, then selects all the matching nodes from the database using pager_query and builds an array containing the matches. Then it returns the HTML for a table containing the matching records using the currently selected theme. Later in the module file there are more functions that handle navigation and processing for the “demo_pattern_form.”
This code is so simple it’s hard to imagine that anything could be wrong with it: in just a few lines it gets the data we need and also displays it. It’s also very typical Drupal module code. If you look around the Drupal code base or at code from 3rd party modules you will see a lot of code that looks just like this. It uses common Drupal functions like “theme,” “pager_query” and “db_fetch_object.”
But in my opinion there are a two things wrong with this example module:
A veteran Drupal developer would disagree with the second point, of course: obviously the SQL statement used in demo_page_view really is the only custom behavior here, and all of the other calls to Drupal functions like drupal_get_form, table_sort_sql, etc., are used to help display our data in a Drupal page. But imagine a PHP developer who wasn’t yet very familiar with Drupal looking at this module file for the first time: how would she or he have any idea what this module did? Or where to begin to change it’s behavior if necessary? This is another important use of tests: as living documentation for what code does and how it should work.
This is a silly example, but imagine if this module did something sophisticated and important to your business. Then imagine if your business decided to upgrade from one version of Drupal to another – or imagine that your business decided to use DJango, Joomla or some other CMS system… or to implement it using custom Java or Ruby code. Then the question would become: what part of the module’s code is ours that we want to keep? And what part of the module is simply needed to coexist with Drupal? Right now it’s not so easy to tell.
I believe that using TDD while writing a Drupal module will not only provide the normal benefits of testing: emergent design, living documentation and simply more robust code, but will also lead to isolating your business’s code from the framework in a natural way. By writing unit tests first, you will have to think about what you are trying to test: your business logic only and not the Drupal framework itself.
In my next post we’ll get started by installing PHPUnit and writing our first PHPUnit unit test with Drupal.
My second visit to Camille was a slightly more sedate affair - aside from a boisterous group of American women, the restaurant was relatively quiet when I tumbled in, fresh from Bar Hemingway, at about 10 o'clock. I was seated at a small table toward the back, and settled in with my book and a pichet (25, not 50 centilitres this time) of my beloved Bordeaux.
I'd filled up a bit on Bar Hemingway's most excellent potato chips, so I decided to forgo a starter and dive right into Camille's legendary (well, to me and Louisa, at the very least) steak tartare. I had to reassure the waiter that I knew the dish n'est pas cuit ("is not cooked"), and said yes to his offer of salad and frites on the side. This was gonna be good.And it was. It was so good that I almost forgot to pause long enough to take a picture - but that would have been a crime, since you all deserve to bear witness to this most gorgeous dish. The steak tartare at Camille remains the best I have ever eaten - and I've eaten my fair share. The silky egg, the cornichons, capers and onions laced throughout the meat, the earthy mustard: all of these are to be expected, and are fantastically in play here.
But there's something else going on in Camille's tartare that I haven't quite been able to pin down - some elusive sweetness that balances out the tang of the traditional ingredients. This balance of sweet and savory is the same thing that makes their duck so delectable, and is, I think, is the thing that makes Camille so exceptional. You don't expect a neighborhood joint to produce something this subtly and intriguingly seasoned - it's a pretty impressive feat.Oh, and the fries? Cooked in duck fat, and completely sublime. Ditto the simple salad, bibb lettuce tossed with a house-made vinaigrette.
Finally, to finish things off nice and proper, I ordered chocolate mousse for dessert. Camille's mousse is dense and rich, made with bittersweet chocolate and not a whole lot of sugar. Which is, for me, pretty close to perfection. I tried to solider through, but only managed to finish about half. My adorable waiter was a bit disappointed, I think. I promise to try harder next time!
I finally got around to upgrading my version of rspec-rails from one that's almost a year old and came across an issue with the way implicit module inclusion is handled.
If you have a handler that uses memoization to cache some information in instance variables such as this (I'm not sure if this is a smell but my project has some examples of it)
module UsersHelper
def all_users
@users ||= User.find(:all)
end
end
describe UsersHelper do
it "should find all users" do
User.expects(:find).with(:all).returns(result=mock)
helper.all_users.should == result
end
end
I finally got around to upgrading my version of rspec-rails from one that's almost a year old and came across an issue with the way implicit module inclusion is handled.
If you have a handler that uses memoization to cache some information in instance variables such as this (I'm not sure if this is a smell but my project has some examples of it)
module UsersHelper
def all_users
@users ||= User.find(:all)
end
end
describe UsersHelper do
it "should find all users" do
User.expects(:find).with(:all).returns(result=mock)
helper.all_users.should == result
end
end
What are your sniff tests to identify a simple a design?.
Here are some thought starters...
- Can the design be explained in a simple sentence?
- Does it use any heavyweight software? ;-) We're living in the era of lightweight technologies
- Can a version of it (scoped down) be released in less than 100 days?
- Any two-way data synchronization?
- Large number of sub-systems?
- ?
I often watch my specs run with the ........'s marching across my terminal and sometimes it seems to pause as it hits a particularly slow one. I figure there's some poorly written spec that's slowing my entire suite down and I should figure out what it is and fix it. But I rarely (if ever) take the time to track down the offender so never fix it. I just discovered that the clever folks writing rspec have taken away my excuse for laziness. I can tell it to report on the slowest specs by putting a line in my spec.opts file telling it to use the ProfileFormatter (see the first line below)
--format profile
--colour
--loadby mtime
--reverse
Profiling enabled.
........(a bunch more dots)
Top 10 slowest examples:
2.4908950 WikiContent should find all activity for a user
0.8061750 WikiContent should find recent activity for a user in last 15 days
0.2438460 WikiContent::Version should allow limit on results from finding recent activity
0.1821710 User should have a list of owned content
0.1700670 WikiContent should find results if access type is not same as role of the user but role of user is 'Leader'
0.1593900 ContentsController allows PUT request to 'update' action for 'member' role
0.1514150 Search::SearchResponse gets the wiki content id of an asset from the xml response
0.1470310 Content should always allow leaders to see all pages
0.1394920 Page should delete all comments when destroyed
0.1370330 ContentsController should save content and create the uploaded asset on successful POST to the 'create' action
I often watch my specs run with the ........'s marching across my terminal and sometimes it seems to pause as it hits a particularly slow one. I figure there's some poorly written spec that's slowing my entire suite down and I should figure out what it is and fix it. But I rarely (if ever) take the time to track down the offender so never fix it. I just discovered that the clever folks writing rspec have taken away my excuse for laziness. I can tell it to report on the slowest specs by putting a line in my spec.opts file telling it to use the ProfileFormatter (see the first line below)
--format profile
--colour
--loadby mtime
--reverse
Profiling enabled.
........(a bunch more dots)
Top 10 slowest examples:
2.4908950 WikiContent should find all activity for a user
0.8061750 WikiContent should find recent activity for a user in last 15 days
0.2438460 WikiContent::Version should allow limit on results from finding recent activity
0.1821710 User should have a list of owned content
0.1700670 WikiContent should find results if access type is not same as role of the user but role of user is 'Leader'
0.1593900 ContentsController allows PUT request to 'update' action for 'member' role
0.1514150 Search::SearchResponse gets the wiki content id of an asset from the xml response
0.1470310 Content should always allow leaders to see all pages
0.1394920 Page should delete all comments when destroyed
0.1370330 ContentsController should save content and create the uploaded asset on successful POST to the 'create' action
When you cannot influence technology decisions that an external team makes, what expectations can you set to still get a quality product?
- Code checked into a version management system (SVN, Git)
- Automated build/test/deployment mechanisms
- 100% test coverage (automated test cases)
These things will ensure the following for application -
- low cost of change
- easy transition to other teams
- easy support
Internally we track such attributes in a product scorecard that we review periodically. You've got one too, don't you?
Often we're asked to consult/advise on technology solutions built within the Firm but not at IT. This isn't rare because the Firm encourages thousand flowers to bloom and also IT cannot meet all software demands in the Firm.
These situations are challenging as the overall cost of building and supporting a solution isn't considered in the effort to build a cheap and effective solution. The existing IT ecosystem does matter. If IT doesn't support a certain product/platform, then using that product/platform has downstream support implications (cost $$$) that are often ignored when pitching a product/platform for a cheap and effective solution.
We need to consider total costs while making these technology decisions, for IT needs to create transparency on support/maintenance costs.
Existing IT ecosystem does matter!
I'm not the only person out there obsessed with both literature and food, and here's the proof. There's a new blog called Lashings and Lashings of Ginger Beer that features a food-focused literary passage each day. So far, they've posted on The Lion, The Witch and The Wardrobe, Goblin Market, Swann's Way (The madeleine, of course!) and many, many others.
So, if you're jonesin' for a fix of food porn and can't wait for Louisa's first novel (Can't Stand the Heat, due out in the fall of 2009 from St. Martin's Press), head on over to Lashings and get your fill!
I don't really visit Paris often enough to have a legitimate favorite restaurant, but, if I did, I feel fairly certain that Bistro Camille would still make the cut. Nestled on a corner of the rue des Francs Bourgeois, impossibly difficult to find on my first two visits and remarkably easy to locate on this one, it's that perfect neighborhood spot: kindly lit, well-provisioned with wine, and full of delicious, simply and beautifully prepared food.
All of this is probably why I found myself there twice in two days on my trip to Paris. Louisa and I discovered Camille on this eGullet thread, and I think it's safe to say that (aside from a chance visit to the devestatingly wonderful Chez Yvonne in Strasbourg) neither of us has ever been happier with a find.My hotel was just a few blocks south of Camille, and the walk through the Marais was lovely both nights. I took slightly different routes to be sure to catch all the shop windows and street life I could. I ate late (by American standards), not until 10:00, since I was jet-lagged and in need of naps. On Saturday, there was still a twenty minute wait for a table, so I sat outside.
I started with the escargots de Bourgogne, a traditional preparation. The snails are cleaned, the shells are stuffed with a mixture of butter, parsley and garlic, and the whole thing is run under the broiler for a few minutes. Good escargots are tender and light, not tough and spongy, and eating them is like eating delicate balls of buttery, garlicky goodness. Their shells seem so fine that you expect them to shatter when you pick them up with the special tongs (don't worry; they won't). You spend more time than you care to admit sopping up the butter with your bread.
These were good escargots.
The waitress talked me into a full demi (half-bottle, 50 centilitres) of Bordeaux, so I was in it for the long haul. I took my time enjoying the brisk fall weather, eavesdropping on the folks to my right (a British woman and two French men, speaking their own private Franglais), and savoring my escargots. Once I'd been able to stop myself from wiping up every last bit of butter from the plate, out came my main.I'd ordered the duck breast, which was seared and served with the most delicious sauce made with pan drippings, honey and black pepper, all piled atop some seriously buttery pureed potatoes. This duck dish is a great example of how true French cooking defies its old American stereotypes - the flavors are extremely well-balanced, with the piquant pepper playing against the round, sweet honey, and the buttery potatoes contrasting with the seared crust on the duck. This is not over-sauced, over-treated food; this is gorgeous ingredients being coaxed into an even more perfect state.
Dessert was similarly delicious. Two years ago, Camille's creme brulée restored our faith in the French, after it had been destroyed by lackluster specimens in Strasbourg and Champagne. Camille still has the touch - the custard is particularly thick and creamy, heavy with vanilla and a touch of citrus. Most importantly, they've mastered the sugar-to-custard ratio: their shallow, wide dish provides lots of surface area for the crackly, caramelized sugar. Smokey, creamy goodness.
Finally, coffee. Camille makes an exceptionally delicious cup. Strong, rich, piping hot and just the right size - helps you settle down post-meal, but doesn't fill you up.
Ah, Camille, je t'aime.
Thatz the first question I hear while talking to a developer who is not writing tests for his/her code.
"Delivering Code Without Tests Is A Sin"
This is a punch line by Bob-Martin, I heard in one of his sessions I attended. We already are wasting/investing huge amount of time in injecting bad design and bugs into our applications. So I want to ask this question again -
Do we have enough time for testing?
My question to this question is - Do we have anything to lose?
Lets start writing tests...
I'm inspired by Hashrocket's (a Obie Fernandez company) 3-2-1 launch idea - "Your idea, implemented in 3 days. No joke."
In our organization, we've decreased software launch duration from 9+ months to 100-days and have now done it consistently for several months now. Its done. We're now programmed to do this now!
Isn't it time to push it down further? Come on! Lets break the 100-day barrier. I'm thinking of 30-days launch and want to try it out if the following are true:
- Its a new web app (existing apps to be considered later)
- Visual design of app (wireframes, etc.) is ready
- App has well defined and limited integration points
- Product Owner is available throughout development
- Well defined testing approach for end users
- Anything else to consider?
I'm willing to give it a shot. Anyone interested?
If you haven't seen Wanted you should (if you like action flicks). It's awesome. Sujit, Praful and I watched it this weekend and loved it. Its pace didn't give you a second to think or relax...
My favorite parts are the scenes where they're curving the bullets and where Wesley says "What the f*** have you done lately?" Inspiring ha?
Is there such a thing as complex application?
I like to believe that the answer is NO.
Let me draw a parallel from project timelines to explain.
Till 2007 we used to have many projects that took 9 - 12 months to release working software. Not anymore. Now, we release working software in production/staging every 100-days. No, we haven't magically shrunk a Document Management or ERP system implementation to 100-days from several months. All it means is that we've broken down the 9 or 12 month project into 3 or 4 100-day releases now. This has simplified a lot of things and resulted in successful delivery of large projects.
Can't we apply the same concept to a "complex application" we need to build and break it up into multiple simple applications/components. Many engineers in our team practice it really well and I'm learning this skill from them. Let me know if you want to join the class!
Last month Prasoon posted thoughts on high performing teams - what is a high performing team, what are their foundational qualities and behaviors.
Here are few more thoughts on what can we do to create high performing teams:

I was working with a bank in the mid-west US a few years back. They were building an on-line mortgage application that would allow brokers to submit loan applications and get real time product and pricing information. After some initial discussions it was decided to build a product and pricing engine in-house using an of-the-shelf [...]
Lots of aggression all around.. questions.. blames.. resignations..
Some groups, citizen showing a high sense of responsibility.. of worrying about the state of country and people..
Some very irresponsible.. thick skinned..
Everyone around me.. at my work, my friends, my family.. express frustration in various ways about such barbaric terror acts..
I've been thinking around all these thoughts, information revealed.. discussions..
I am a lazy person.. And I know this very well, I publicly accept this. This actually is very helpful in always being aware of this negative quality of mine. As it helps me remain alert at many times. But generally I am lazy about many things..
- my health,
- my plans towards my career,
- maintaining documents,
- filing my tax returns,
- playing with my kid,
- calling my friends,
- wishing my friends happy birthday,
The list goes on..
But still I am leading a very responsible and efficient daily life. I continue taking care of my health, my family, my social responsibilities, my career, my documents... list goes on.
How?
I am part of Cycle Of Life. The way I understand it is, there is a threshold, for every negligence, every ignorance, every selfish act, every uncaring attitude. As soon as the threshold is reached, the alarm goes ringing. I dont really care at the first alarm. But there are series of alarms well set, which keep going on with increasing threshold values. And a time comes when a blast is triggered. The blast is very harmful all the time. I have to pay heavy penalty to my bank for not taking care of my documents, I catch cold/fewer for neglecting my health, my kid starts becomming cranky and unstoppable because I am not paying attention to him...
After the first instance of each blast I start taking care to avoid it to repeat.
Our society behaves in exactly the same way. People all around the world become selfish, irresponsible, careless. If they do not listen to warning alarms they suffer thru the blasts. In the news / papers, I can read a lot about various agencies across different countries showing irresponsible behaviour in executing there duties. Of-course they are all headed by human beings who commit mistakes.
If we see the current scenario around the world, the number of alarm blasts is very high.
- multiple economies going down
- financial crisis
- terrorist activities
- etc...
This will cause damage at various levels. And bring the world below the threshold level of alarms.
I believe, the divine power can help us at the micro-est level to take care of preventing us from doing any negative act.
I wish we sahajayogis are able to convey the vision of Sahajayoga to the world.
Sahajayoga principles are the only way we can always remain below the threshold and keep growing continuously in positive direction.
By Sahayoga principles I mean -
- How can I avoid lying?
- How can I get more strength to forgive?
- How can I only love and never hate?
A beautiful world visioned by Shri Mataji Nirmala Devi. You'll see this coming true if you meet with a group of Sahajayogis.
http://www.sahajayoga.org/
Traditionally web application development has required web developer(s) and database developer(s).
Those web developers are dying or already dead now.
They're being replaced by a new breed of "web application developers" who can do much more than their predecessors. This new specie can develop a web application from A to Z... from top to bottom. They possess not only the typical web development skills but also enough database skills to crank out a web application of simple to medium complexity all by themselves.
This trend is here to stay... When I look back, till about 2 - 3 years ago all development teams needed a database developer; now some teams build an application without a database specialist.
There are many reasons for this shift including maturity in technologists (polyskilled) and technology (abstraction of database in frameworks like Ruby on Rails) etc.
In my last post I talked about encrypting RESTful urls when we have to expose primary key in the URL. The plugin is generic and exposes handy methods.
Repository:
http://github.com/toamitkumar/url_encrypt/tree/master
Installation:
.scirpt/plugin install git://github.com/toamitkumar/url_encrypt.git
Usage:
Add the following line to your environment.rb file
UrlEncrypt.encryptors("abcdefghijklmnop", "mnbkjhkhkhkhkhkjhkjh")
#so that KEY and IV for Cipher encryption are different
OR
UrlEncrypt.encryptors("abcdefghijklmnop")
# so that KEY and IV for Cipher encryption are same
OR
NOTHING
Inside your model add the line:
class Book < ActiveRecord::Basecolumn :id, :integer
column :title, :string
encrypted :with => :title
end
You have handy methods:Book.find_by_encrypted_title('encrypted string')
Book.find_by_encrypted_title('encrypted string',
:conditions => ["any other condition can go here"])
Next step: To roll out the plugin as JRuby.
Some time ago, I blogged about the difference between in-the-flow and above-the-flow uses of wikis and social software more broadly. At the time, I argued that "above-the-flow" use cases fail to generate adoption for the same reason that knowledge management...
Social software is changing in ways that profoundly impact the way companies should approach adoption. A year ago, the focus was on individual technologies (wikis, blogs, RSS, etc.). We are rapidly evolving to more comprehensive solutions that integrate multiple technologies...
Mark wrote about variety in work and I subscribe to that philosophy as well. I believe we all undergo a constant cycle of Learn -> Practice -> Teach in our lives and keep creating new cycles periodically...
In fact, I'm encouraging people I work with to be explicit about their Learn -> Practice -> Teach cycles and share it (their 2009 "development plan") broadly. Yes, why not get everyone's help in having a successful year, why not declare - what we're working on, what we're getting better at and what we've experienced already.
Off-late, I have been scouting in Open source community to find tools which can be useful in my organization. Yesterday, my colleague mentioned that his team is looking for an alternative to our enterprise CMS. That encouraged me to focus on CMS this time, just so as to make my efforts worthwhile.
|