Archive for the ‘hurdle’ tag
Weekend Project: Sharing a WordPress War Story
While we love blogging, we all know there are some aspects that really do seem impossible sometimes—none moreso than transferring a WordPress.com blog to the WordPress.org platform.
We’ve discussed the differences between these two platforms before, because more than one blogger has been caught up by the limitations of WordPress.com (usually the limitation that this platform doesn’t allow you to monetize your blog). But it’s well known that swapping to the .org platform from .com can be a challenge.
This weekend’s project explains the WordPress war story of a blogger who chose to start a blog on WordPress.com, because it required so little technical knowledge. But when she wanted to monetize her blog—and switch to the .org platform—that lack of technical skill proved a major hurdle. It’s no wonder the process has gained such a bad reputation!
Actually, I think this is something that blog platform developers probably want to consider as they’re creating their platforms‚ because any help they can give to users who want to upgrade or switch to other versions of their products is always much appreciated.
If you’re one of those bloggers who’s itching to move your blog from .com to .org, but you’ve been too scared, clear some time in your weekend schedule to implement the process that our Weekend Project sets out. I’m giving you plenty of warning for this project—it starts tomorrow!
For now, if you have a WordPress war story of your own that you’d like to get off your chest, feel free to vent in the comments.
Originally at: Blog Tips at ProBlogger
Weekend Project: Sharing a WordPress War Story
Introduction To JavaScript Unit Testing
You probably know that testing is good, but the first hurdle to overcome when trying to write unit tests for client-side code is the lack of any actual units; JavaScript code is written for each page of a website or each module of an application and is closely intermixed with back-end logic and related HTML. In the worst case, the code is completely mixed with HTML, as inline events handlers.
This is likely the case when no JavaScript library for some DOM abstraction is being used; writing inline event handlers is much easier than using the DOM APIs to bind those events. More and more developers are picking up a library such as jQuery to handle the DOM abstraction, allowing them to move those inline events to distinct scripts, either on the same page or even in a separate JavaScript file. However, putting the code into separate files doesn’t mean that it is ready to be tested as a unit.
What is a unit anyway? In the best case, it is a pure function that you can deal with in some way — a function that always gives you the same result for a given input. This makes unit testing pretty easy, but most of the time you need to deal with side effects, which here means DOM manipulations. It’s still useful to figure out which units we can structure our code into and to build unit tests accordingly.
Building Unit Tests
With that in mind, we can obviously say that starting with unit testing is much easier when starting something from scratch. But that’s not what this article is about. This article is to help you with the harder problem: extracting existing code and testing the important parts, potentially uncovering and fixing bugs in the code.
The process of extracting code and putting it into a different form, without modifying its current behavior, is called refactoring. Refactoring is an excellent method of improving the code design of a program; and because any change could actually modify the behaviour of the program, it is safest to do when unit tests are in place.
This chicken-and-egg problem means that to add tests to existing code, you have to take the risk of breaking things. So, until you have solid coverage with unit tests, you need to continue manually testing to minimize that risk.
That should be enough theory for now. Let’s look at a practical example, testing some JavaScript code that is currently mixed in with and connected to a page. The code looks for links with title attributes, using those titles to display when something was posted, as a relative time value, like “5 days ago”:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Mangled date examples</title>
<script>
function prettyDate(time){
var date = new Date(time || ""),
diff = ((new Date().getTime() - date.getTime()) / 1000),
day_diff = Math.floor(diff / 86400);
if (isNaN(day_diff) || day_diff < 0 || day_diff >= 31) {
return;
}
return day_diff == 0 && (
diff < 60 && "just now" ||
diff < 120 && "1 minute ago" ||
diff < 3600 && Math.floor( diff / 60 ) + " minutes ago" ||
diff < 7200 && "1 hour ago" ||
diff < 86400 && Math.floor( diff / 3600 ) + " hours ago") ||
day_diff == 1 && "Yesterday" ||
day_diff < 7 && day_diff + " days ago" ||
day_diff < 31 && Math.ceil( day_diff / 7 ) + " weeks ago";
}
window.onload = function(){
var links = document.getElementsByTagName("a");
for (var i = 0; i < links.length; i++) {
if (links[i].title) {
var date = prettyDate(links[i].title);
if (date) {
links[i].innerHTML = date;
}
}
}
};
</script>
</head>
<body>
<ul>
<li class="entry" id="post57">
<p>blah blah blah…</p>
<small class="extra">
Posted <a href="/2008/01/blah/57/" title="2008-01-28T20:24:17Z">January 28th, 2008</a>
by <a href="/john/">John Resig</a>
</small>
</li>
<!-- more list items -->
</ul>
</body>
</html>
If you ran that example, you’d see a problem: none of the dates get replaced. The code works, though. It loops through all anchors on the page and checks for a title property on each. If there is one, it passes it to the prettyDate function. If prettyDate returns a result, it updates the innerHTML of the link with the result.
Make Things Testable
The problem is that for any date older then 31 days, prettyDate just returns undefined (implicitly, with a single return statement), leaving the text of the anchor as is. So, to see what’s supposed to happen, we can hardcode a “current” date:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Mangled date examples</title>
<script>
function prettyDate(now, time){
var date = new Date(time || ""),
diff = (((new Date(now)).getTime() - date.getTime()) / 1000),
day_diff = Math.floor(diff / 86400);
if (isNaN(day_diff) || day_diff < 0 || day_diff >= 31) {
return;
}
return day_diff == 0 && (
diff < 60 && "just now" ||
diff < 120 && "1 minute ago" ||
diff < 3600 && Math.floor( diff / 60 ) + " minutes ago" ||
diff < 7200 && "1 hour ago" ||
diff < 86400 && Math.floor( diff / 3600 ) + " hours ago") ||
day_diff == 1 && "Yesterday" ||
day_diff < 7 && day_diff + " days ago" ||
day_diff < 31 && Math.ceil( day_diff / 7 ) + " weeks ago";
}
window.onload = function(){
var links = document.getElementsByTagName("a");
for (var i = 0; i < links.length; i++) {
if (links[i].title) {
var date = prettyDate("2008-01-28T22:25:00Z", links[i].title);
if (date) {
links[i].innerHTML = date;
}
}
}
};
</script>
</head>
<body>
<ul>
<li class="entry" id="post57">
<p>blah blah blah…</p>
<small class="extra">
Posted <a href="/2008/01/blah/57/" title="2008-01-28T20:24:17Z">January 28th, 2008</a>
by <a href="/john/">John Resig</a>
</small>
</li>
<!-- more list items -->
</ul>
</body>
</html>
Now, the links should say “2 hours ago,” “Yesterday” and so on. That’s something, but still not an actual testable unit. So, without changing the code further, all we can do is try to test the resulting DOM changes. Even if that did work, any small change to the markup would likely break the test, resulting in a really bad cost-benefit ratio for a test like that.
Refactoring, Stage 0
Instead, let’s refactor the code just enough to have something that we can unit test.
We need to make two changes for this to happen: pass the current date to the prettyDate function as an argument, instead of having it just use new Date, and extract the function to a separate file so that we can include the code on a separate page for unit tests.
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Refactored date examples</title>
<script src="prettydate.js"></script>
<script>
window.onload = function() {
var links = document.getElementsByTagName("a");
for ( var i = 0; i < links.length; i++ ) {
if (links[i].title) {
var date = prettyDate("2008-01-28T22:25:00Z", links[i].title);
if (date) {
links[i].innerHTML = date;
}
}
}
};
</script>
</head>
<body>
<ul>
<li class="entry" id="post57">
<p>blah blah blah…</p>
<small class="extra">
Posted <a href="/2008/01/blah/57/" title="2008-01-28T20:24:17Z">January 28th, 2008</a>
by <a href="/john/">John Resig</a>
</small>
</li>
<!-- more list items -->
</ul>
</body>
</html>
Here’s the contents of prettydate.js:
function prettyDate(now, time){
var date = new Date(time || ""),
diff = (((new Date(now)).getTime() - date.getTime()) / 1000),
day_diff = Math.floor(diff / 86400);
if (isNaN(day_diff) || day_diff < 0 || day_diff >= 31) {
return;
}
return day_diff == 0 && (
diff < 60 && "just now" ||
diff < 120 && "1 minute ago" ||
diff < 3600 && Math.floor( diff / 60 ) + " minutes ago" ||
diff < 7200 && "1 hour ago" ||
diff < 86400 && Math.floor( diff / 3600 ) + " hours ago") ||
day_diff == 1 && "Yesterday" ||
day_diff < 7 && day_diff + " days ago" ||
day_diff < 31 && Math.ceil( day_diff / 7 ) + " weeks ago";
}
Now that we have something to test, let’s write some actual unit tests:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Refactored date examples</title>
<script src="prettydate.js"></script>
<script>
function test(then, expected) {
results.total++;
var result = prettyDate("2008-01-28T22:25:00Z", then);
if (result !== expected) {
results.bad++;
console.log("Expected " + expected + ", but was " + result);
}
}
var results = {
total: 0,
bad: 0
};
test("2008/01/28 22:24:30", "just now");
test("2008/01/28 22:23:30", "1 minute ago");
test("2008/01/28 21:23:30", "1 hour ago");
test("2008/01/27 22:23:30", "Yesterday");
test("2008/01/26 22:23:30", "2 days ago");
test("2007/01/26 22:23:30", undefined);
console.log("Of " + results.total + " tests, " + results.bad + " failed, "
+ (results.total - results.bad) + " passed.");
</script>
</head>
<body>
</body>
</html>
- Run this example. (Make sure to enable a console such as Firebug or Chrome’s Web Inspector.)
This will create an ad-hoc testing framework, using only the console for output. It has no dependencies to the DOM at all, so you could just as well run it in a non-browser JavaScript environment, such as Node.js or Rhino, by extracting the code in the script tag to its own file.
If a test fails, it will output the expected and actual result for that test. In the end, it will output a test summary with the total, failed and passed number of tests.
If all tests have passed, like they should here, you would see the following in the console:
Of 6 tests, 0 failed, 6 passed.
To see what a failed assertion looks like, we can change something to break it:
Expected 2 day ago, but was 2 days ago.
Of 6 tests, 1 failed, 5 passed.
While this ad-hoc approach is interesting as a proof of concept (you really can write a test runner in just a few lines of code), it’s much more practical to use an existing unit testing framework that provides better output and more infrastructure for writing and organizing tests.
The QUnit JavaScript Test Suite
The choice of framework is mostly a matter of taste. For the rest of this article, we’ll use QUnit (pronounced “q-unit”), because its style of describing tests is close to that of our ad-hoc test framework.
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Refactored date examples</title>
<link rel="stylesheet" href="qunit.css" />
<script src="qunit.js"></script>
<script src="prettydate.js"></script>
<script>
test("prettydate basics", function() {
var now = "2008/01/28 22:25:00";
equal(prettyDate(now, "2008/01/28 22:24:30"), "just now");
equal(prettyDate(now, "2008/01/28 22:23:30"), "1 minute ago");
equal(prettyDate(now, "2008/01/28 21:23:30"), "1 hour ago");
equal(prettyDate(now, "2008/01/27 22:23:30"), "Yesterday");
equal(prettyDate(now, "2008/01/26 22:23:30"), "2 days ago");
equal(prettyDate(now, "2007/01/26 22:23:30"), undefined);
});
</script>
</head>
<body>
<div id="qunit"></div>
</body>
</html>
Three sections are worth a closer look here. Along with the usual HTML boilerplate, we have three included files: two files for QUnit (qunit.css and qunit.js) and the previous prettydate.js.
Then, there’s another script block with the actual tests. The test method is called once, passing a string as the first argument (naming the test) and passing a function as the second argument (which will run the actual code for this test). This code then defines the now variable, which gets reused below, then calls the equal method a few times with varying arguments. The equal method is one of several assertions that QUnit provides. The first argument is the result of a call to prettyDate, with the now variable as the first argument and a date string as the second. The second argument to equal is the expected result. If the two arguments to equal are the same value, then the assertion will pass; otherwise, it will fail.
Finally, in the body element is some QUnit-specific markup. These elements are optional. If present, QUnit will use them to output the test results.
The result is this:
With a failed test, the result would look something like this:
Because the test contains a failing assertion, QUnit doesn’t collapse the results for that test, and we can see immediately what went wrong. Along with the output of the expected and actual values, we get a diff between the two, which can be useful for comparing larger strings. Here, it’s pretty obvious what went wrong.
Refactoring, Stage 1
The assertions are currently somewhat incomplete because we aren’t yet testing the n weeks ago variant. Before adding it, we should consider refactoring the test code. Currently, we are calling prettyDate for each assertion and passing the now argument. We could easily refactor this into a custom assertion method:
test("prettydate basics", function() {
function date(then, expected) {
equal(prettyDate("2008/01/28 22:25:00", then), expected);
}
date("2008/01/28 22:24:30", "just now");
date("2008/01/28 22:23:30", "1 minute ago");
date("2008/01/28 21:23:30", "1 hour ago");
date("2008/01/27 22:23:30", "Yesterday");
date("2008/01/26 22:23:30", "2 days ago");
date("2007/01/26 22:23:30", undefined);
});
Here we’ve extracted the call to prettyDate into the date function, inlining the now variable into the function. We end up with just the relevant data for each assertion, making it easier to read, while the underlying abstraction remains pretty obvious.
Testing The DOM manipulation
Now that the prettyDate function is tested well enough, let’s shift our focus back to the initial example. Along with the prettyDate function, it also selected some DOM elements and updated them, within the window load event handler. Applying the same principles as before, we should be able to refactor that code and test it. In addition, we’ll introduce a module for these two functions, to avoid cluttering the global namespace and to be able to give these individual functions more meaningful names.
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Refactored date examples</title>
<link rel="stylesheet" href="qunit.css" />
<script src="qunit.js"></script>
<script src="prettydate2.js"></script>
<script>
test("prettydate.format", function() {
function date(then, expected) {
equal(prettyDate.format("2008/01/28 22:25:00", then), expected);
}
date("2008/01/28 22:24:30", "just now");
date("2008/01/28 22:23:30", "1 minute ago");
date("2008/01/28 21:23:30", "1 hour ago");
date("2008/01/27 22:23:30", "Yesterday");
date("2008/01/26 22:23:30", "2 days ago");
date("2007/01/26 22:23:30", undefined);
});
test("prettyDate.update", function() {
var links = document.getElementById("qunit-fixture").getElementsByTagName("a");
equal(links[0].innerHTML, "January 28th, 2008");
equal(links[2].innerHTML, "January 27th, 2008");
prettyDate.update("2008-01-28T22:25:00Z");
equal(links[0].innerHTML, "2 hours ago");
equal(links[2].innerHTML, "Yesterday");
});
test("prettyDate.update, one day later", function() {
var links = document.getElementById("qunit-fixture").getElementsByTagName("a");
equal(links[0].innerHTML, "January 28th, 2008");
equal(links[2].innerHTML, "January 27th, 2008");
prettyDate.update("2008-01-28T22:25:00Z");
equal(links[0].innerHTML, "Yesterday");
equal(links[2].innerHTML, "2 days ago");
});
</script>
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture">
<ul>
<li class="entry" id="post57">
<p>blah blah blah…</p>
<small class="extra">
Posted <span class="time"><a href="/2008/01/blah/57/" title="2008-01-28T20:24:17Z">January 28th, 2008</a></span>
by <span class="author"><a href="/john/">John Resig</a></span>
</small>
</li>
<li class="entry" id="post57">
<p>blah blah blah…</p>
<small class="extra">
Posted <span class="time"><a href="/2008/01/blah/57/" title="2008-01-27T22:24:17Z">January 27th, 2008</a></span>
by <span class="author"><a href="/john/">John Resig</a></span>
</small>
</li>
</ul>
</div>
</body>
</html>
Here’s the contents of prettydate2.js:
var prettyDate = {
format: function(now, time){
var date = new Date(time || ""),
diff = (((new Date(now)).getTime() - date.getTime()) / 1000),
day_diff = Math.floor(diff / 86400);
if (isNaN(day_diff) || day_diff < 0 || day_diff >= 31) {
return;
}
return day_diff === 0 && (
diff < 60 && "just now" ||
diff < 120 && "1 minute ago" ||
diff < 3600 && Math.floor( diff / 60 ) + " minutes ago" ||
diff < 7200 && "1 hour ago" ||
diff < 86400 && Math.floor( diff / 3600 ) + " hours ago") ||
day_diff === 1 && "Yesterday" ||
day_diff < 7 && day_diff + " days ago" ||
day_diff < 31 && Math.ceil( day_diff / 7 ) + " weeks ago";
},
update: function(now) {
var links = document.getElementsByTagName("a");
for ( var i = 0; i < links.length; i++ ) {
if (links[i].title) {
var date = prettyDate.format(now, links[i].title);
if (date) {
links[i].innerHTML = date;
}
}
}
}
};
The new prettyDate.update function is an extract of the initial example, but with the now argument to pass through to prettyDate.format. The QUnit-based test for that function starts by selecting all a elements within the #qunit-fixture element. In the updated markup in the body element, the <div id="qunit-fixture">…</div> is new. It contains an extract of the markup from our initial example, enough to write useful tests against. By putting it in the #qunit-fixture element, we don’t have to worry about DOM changes from one test affecting other tests, because QUnit will automatically reset the markup after each test.
Let’s look at the first test for prettyDate.update. After selecting those anchors, two assertions verify that these have their initial text values. Afterwards, prettyDate.update is called, passing along a fixed date (the same as in previous tests). Afterwards, two more assertions are run, now verifying that the innerHTML property of these elements have the correctly formatted date, “2 hours ago” and “Yesterday.”
Refactoring, Stage 2
The next test, prettyDate.update, one day later, does nearly the same thing, except that it passes a different date to prettyDate.update and, therefore, expects different results for the two links. Let’s see if we can refactor these tests to remove the duplication.
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Refactored date examples</title>
<link rel="stylesheet" href="qunit.css" />
<script src="qunit.js"></script>
<script src="prettydate2.js"></script>
<script>
test("prettydate.format", function() {
function date(then, expected) {
equal(prettyDate.format("2008/01/28 22:25:00", then), expected);
}
date("2008/01/28 22:24:30", "just now");
date("2008/01/28 22:23:30", "1 minute ago");
date("2008/01/28 21:23:30", "1 hour ago");
date("2008/01/27 22:23:30", "Yesterday");
date("2008/01/26 22:23:30", "2 days ago");
date("2007/01/26 22:23:30", undefined);
});
function domtest(name, now, first, second) {
test(name, function() {
var links = document.getElementById("qunit-fixture").getElementsByTagName("a");
equal(links[0].innerHTML, "January 28th, 2008");
equal(links[2].innerHTML, "January 27th, 2008");
prettyDate.update(now);
equal(links[0].innerHTML, first);
equal(links[2].innerHTML, second);
});
}
domtest("prettyDate.update", "2008-01-28T22:25:00Z:00", "2 hours ago", "Yesterday");
domtest("prettyDate.update, one day later", "2008-01-29T22:25:00Z:00", "Yesterday", "2 days ago");
</script>
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture">
<ul>
<li class="entry" id="post57">
<p>blah blah blah…</p>
<small class="extra">
Posted <span class="time"><a href="/2008/01/blah/57/" title="2008-01-28T20:24:17Z">January 28th, 2008</a></span>
by <span class="author"><a href="/john/">John Resig</a></span>
</small>
</li>
<li class="entry" id="post57">
<p>blah blah blah…</p>
<small class="extra">
Posted <span class="time"><a href="/2008/01/blah/57/" title="2008-01-27T22:24:17Z">January 27th, 2008</a></span>
by <span class="author"><a href="/john/">John Resig</a></span>
</small>
</li>
</ul>
</div>
</body>
</html>
Here we have a new function called domtest, which encapsulates the logic of the two previous calls to test, introducing arguments for the test name, the date string and the two expected strings. It then gets called twice.
Back To The Start
With that in place, let’s go back to our initial example and see what that looks like now, after the refactoring.
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Final date examples</title>
<script src="prettydate2.js"></script>
<script>
window.onload = function() {
prettyDate.update("2008-01-28T22:25:00Z");
};
</script>
</head>
<body>
<ul>
<li class="entry" id="post57">
<p>blah blah blah…</p>
<small class="extra">
Posted <span class="time"><a href="/2008/01/blah/57/" title="2008-01-28T20:24:17Z"><span>January 28th, 2008</span></a></span>
by <span class="author"><a href="/john/">John Resig</a></span>
</small>
</li>
<li class="entry" id="post57">
<p>blah blah blah…</p>
<small class="extra">
Posted <span class="time"><a href="/2008/01/blah/57/" title="2008-01-27T22:24:17Z"><span>January 27th, 2008</span></a></span>
by <span class="author"><a href="/john/">John Resig</a></span>
</small>
</li>
<li class="entry" id="post57">
<p>blah blah blah…</p>
<small class="extra">
Posted <span class="time"><a href="/2008/01/blah/57/" title="2008-01-26T22:24:17Z"><span>January 26th, 2008</span></a></span>
by <span class="author"><a href="/john/">John Resig</a></span>
</small>
</li>
<li class="entry" id="post57">
<p>blah blah blah…</p>
<small class="extra">
Posted <span class="time"><a href="/2008/01/blah/57/" title="2008-01-25T22:24:17Z"><span>January 25th, 2008</span></a></span>
by <span class="author"><a href="/john/">John Resig</a></span>
</small>
</li>
<li class="entry" id="post57">
<p>blah blah blah…</p>
<small class="extra">
Posted <span class="time"><a href="/2008/01/blah/57/" title="2008-01-24T22:24:17Z"><span>January 24th, 2008</span></a></span>
by <span class="author"><a href="/john/">John Resig</a></span>
</small>
</li>
<li class="entry" id="post57">
<p>blah blah blah…</p>
<small class="extra">
Posted <span class="time"><a href="/2008/01/blah/57/" title="2008-01-14T22:24:17Z"><span>January 14th, 2008</span></a></span>
by <span class="author"><a href="/john/">John Resig</a></span>
</small>
</li>
<li class="entry" id="post57">
<p>blah blah blah…</p>
<small class="extra">
Posted <span class="time"><a href="/2008/01/blah/57/" title="2008-01-04T22:24:17Z"><span>January 4th, 2008</span></a></span>
by <span class="author"><a href="/john/">John Resig</a></span>
</small>
</li>
<li class="entry" id="post57">
<p>blah blah blah…</p>
<small class="extra">
Posted <span class="time"><a href="/2008/01/blah/57/" title="2007-12-15T22:24:17Z"><span>December 15th, 2008</span></a></span>
by <span class="author"><a href="/john/">John Resig</a></span>
</small>
</li>
</ul>
</body>
</html>
- Run this example.
For a non-static example, we’d remove the argument to prettyDate.update. All in all, the refactoring is a huge improvement over the first example. And thanks to the prettyDate module that we introduced, we can add even more functionality without clobbering the global namespace.
Conclusion
Testing JavaScript code is not just a matter of using some test runner and writing a few tests; it usually requires some heavy structural changes when applied to code that has been tested only manually before. We’ve walked through an example of how to change the code structure of an existing module to run some tests using an ad-hoc testing framework, then replacing that with a more full-featured framework to get useful visual results.
QUnit itself has a lot more to offer, with specific support for testing asynchronous code such as timeouts, AJAX and events. Its visual test runner helps to debug code by making it easy to rerun specific tests and by providing stack traces for failed assertions and caught exceptions. For further reading, check out the QUnit Cookbook.
(al) (km)
© Joern Zaefferer for Smashing Magazine, 2012.
Death To The Install! Play Facebook Games Straight From News Feed
People don’t want to install and give data permissions to games, they want to play them, so now Facebook is allowing games to be played directly from within news feed or Timeline stories. These previews give gamers a taste and could get them over the install hurdle once they’re already addicted.
Facebook has to keep delivering growth for apps to get developers to stick around, or better yet, build for it first. Feed gaming could be a big selling point that could get devs to prioritize Facebook’s canvas over iOS, Android, Chrome web store, and other platforms that force people to download and install before the fun starts. Some of Facebook’s most popular games are already using this tactic, and you can try it out here for Angry Birds, Bubble Witch Saga, and Idle Worship.
Facebook tested feed gaming with Angry Birds earlier this month, but now any games can publish playable feed stories. They show up with a little play button over a thumbnail image in the feed or timeline, but instead of starting up a video, a flash embed of the game opens up. You can instantly start flinging birds, shooting bubbles, popping penguins or whatever. When the game round finishes, you’re often prompted to click through and install / give permissions to the full version.
You can try out feed gaming here for Angry Birds, Bubble Witch Saga, and Idle Worship.
This is going to work. The friction of having the decide whether a developer deserves your data can’t be understated. It probably drives away a ton of potential gamers. Even though most games are freemium and don’t cost anything to download, the try before you buy option means you don’t have to invest until you’re more sure you’ll actually enjoy a game.
Expect this to become a core part of the growth strategy for Facebook games. Developers will need to decide how to distill their games into a 30-second mini-experience. For some twitch games that will be easy, but for deeper strategy games it will be a challenge. They’ll also need to come up with hooks like “beat your friend’s high score” or “earn extra virtual goods by playing from the feed”.
I think people still feel guilty playing Facebook canvas games. These aren’t mobile where you can justify playing because you’re doing so on the move when you’re unable to be productive. These are web games that may be distracting you from your work, or at least communicating with your friends. When you click a link and the first thing you see is a permissions prompt, that guilt is triggered, and you shy away instead of installing.
By delaying the guilt trip data permission until after you’re already having fun, you’re much more likely to speed through the install process to get your next gaming fix. Even the most studious, serious, buttoned down business men are now going to try Facebook games, and some will end up installing and paying.
Display – PPC’s Not So New Neighbor
“I am always doing that which I cannot do, in order that I may learn how to do it.” Pablo Picasso I’ve written before about the issue of maximizing paid search and where to turn to next. Many advertisers understand that display advertising is an option. But the real hurdle to expanding beyond the paid [...]
Follow SEJ on Twitter @sejournal
Sincerely’s Mobile Postcards Now Offer Tracking, Scannable Stamps & A Little Something Called “Magic”
Sincerely, the startup behind a range of photo-printing and greeting card mobile apps, including Postagram, PopBooth, Sincerely Ink and Dotti, is today rolling out a major update to its Postagram product which will introduce post office tracking for its cards as well as scannable QR codes – which the company is cleverly not planning to refer to as “QR codes” (too geeky) in its communications with customers.
More importantly, Postagram is the first of the company’s apps to gain access to “Sincerely Magic,” a new feature that will help build a connected database of the apps’ users across all Sincerely products. The feature will allow users to send cards via snail mail to anyone, even if they only know their email address.
I’m sure we’ve all been the recipient of those emails where a friend or family member asks, “hey, can I get everyone’s mailing address?” Addresses used to be something we remembered, or at least had jotted down in a book somewhere, but in the post-email/Facebook era, they’re an afterthought. You tend to only realize you need a address on the rare occasion you have formal invites to send out, printed photos to share, or something else – like a gift – that needs to be sent via the mail.
There have been a lot of attempts at building an email and/or mobile photo-to-postal mail solution, including SnailMailMyEmail, Postful, Hipster, Red Stamp, and new entry (and more direct competitor to Postagram) Postcardly, to name a few. But many of these have failed to overcome the biggest hurdle in using snail mail – getting people’s addresses.
That’s why what Sincerely is doing is so clever. It automatically searches your phone’s address book (with your permission, of course), to load up Postagram with a list of all the other Sincerely users you’re connected to, filling out your Sincerely address book. For now, the app matches contacts based on email address, but, in the future, being Facebook friends will be all that it takes. The addresses will be marked with a red “S” to separate them from those you added manually.
Postagram (for iPhone) is the first of the company’s apps to get the “Magic” feature, which will roll out to all the company’s products and supported platforms in the coming weeks.
While Sincerely Magic is the biggest news in terms of the startup’s future vision, the Postagram app has also received some other notable features in today’s update. The postcards will now ship with a scannable QR code, which the company will tell customers is “just a stamp.” Scanning the stamp gives the recipient a digital copy of the photo on the card, adds the sender’s address to the recipient’s Sincerely address book, and it alerts the sender the card has been viewed.
Both parties get one free credit towards their next Sincerely purchase, too, after the scan.
Finally, Postagram has added post office tracking, similar to Apple’s Cards app. Senders are alerted as to when their card reaches the first post office center, and when it gets to the recipient’s post office.
Although Apple’s Cards app also provides notifications (via push), Sincerely’s CEO Matt Brezina says that he’s not worried about competing in the same space as Apple. Not only was Sincerely launched first, its Cards competitor Sincerely Ink offers a better selection with better designs. (That’s actually true). As a startup, they’re also able to move more quickly, he says, introducing new features and designs faster than a large company like Apple can. If anything, Brezina says that the launch of Cards was some of the best marketing for Sincerely that they could have gotten.
Printed out photos and cards sent via mobile is just the beginning for this startup. Brezina tells us he sees what they’re building as a “gifting” company, which leaves room to move into other types of gift options via mobile in the near future. While he wasn’t ready to discuss details, some obvious gift options could be flowers or gift cards, perhaps.
Sincerely is backed by $3 million in funding from Spark, First Round, Charles River, and SV Angel.
The updated Postagram app for iOS and Android is rolling out today. (Note that “Magic” is iPhone-only for now).
Here’s What I Believe…
…that companies need to stop focusing on the tools, and start focusing on the connections that the tools help facilitate. It’s not about understanding Twitter or Facebook or Instagram, it’s about understanding customer behavior. Anyone that tries to tell you differently is selling something.
…that companies will get the biggest benefit from emerging digital technologies if they work within the framework of the customer’s existing behavior. Figure out why you customers are spending their time with these channels and tools, then you can figure out how to connect with them in a way that creates value for them.
…that participating in a conversation changes that conversation. Don’t like the conversation happening around your brand? Then start participating in that conversation, and change it.
…that buzzwords are a hurdle to understanding. Speak in as simple terms as possible to explain your ideas. If you use too many buzzwords and jargon you risk limiting understanding of your message. Or worse, you may convince me that YOU don’t understand the concepts you are discussing.
…that customers don’t want to be mouthpieces for brands. Stop viewing Social Media as a ‘new and exciting way to let customers tell our story!’ Your customers have their own stories to tell via Social Media, and they are far more interesting than yours.
…that Twitter isn’t a Social Media Strategy, it’s a Social Media tactic. Tactics are what you use to accomplish a strategy.
…that Steve Knox was right, victory in marketing doesn’t happen when you sell something, but when you cultivate advocates for your brand.
…that customers deserve more than companies are giving them. They deserve brands that understand them and embrace them and give them a reason to fall madly in love with them.
…that Marketing is ultimately a tax that brands pay for not speaking in the voice of their customers. Understand your customers, speak in their voice, and you’ll win their loyalty and money.
…that we need fewer conversations. Brands have two distinct conversations happening around them, the internal conversation they have about themselves, and the external one their customers are having. The further apart these conversations are, the more trouble the brand is in. The more aligned the conversations are, the stronger the brand. Hugh was right.
…that the customer’s ability to smell bullshit is greater than your ability to sell it. So please stop.
…that companies need to stop selling the product, and start selling the benefit. Make your communications customer-centric. Think about WHY I would buy your product and how I would use it, and you just might convince me that I need it.
…that companies need to stop worry about ‘acquiring’ new customers, and focus on delighting their existing ones. New customers cost 6-7 times more to acquire versus retaining an existing customer, while fans spend more than the average customer, and refer business equal to almost half what they spend. Yet marketers everywhere want ‘new’ customers, even at the expense of their existing ones. This is madness.
…that Rockstars have figured out that they’ll get new customers tomorrow from delighting their existing fans, today. And they won’t pay a penny in ‘acquisition’ fees. I’m amazed that more brands aren’t learning from this approach.
…that if you believe in your customers, they will believe in you. Stop treating them like anonymous numbers, they are real people living real lives every day. Just like you.
…that brands need to stop putting the spotlight on themselves. Put the spotlight on the people that make your brand amazing; Your customers and employees.
…that customers are more connected and empowered than ever before. So are the brands that embrace them.
Put Your Annual Checkups on Autopilot and Stop Stressing Over Appointments You’ve Been Meaning to Make [Health]
We all know we’re supposed go to the dentist twice a year, and that most of us should get an annual physical. Depending on your age, there may be other regular tests or preventative screenings you should get. Even though you know you should go, there’s always some reason not to. Maybe you forgot to take a day off work or schedule the appointment in the first place; maybe simply digging up the doctor’s number is a big enough hurdle when the time comes. Let’s fix that right now. Open your favorite calendar and use this guide to make sure you never miss a checkup or important test again. More »
Ad agencies don’t run many ads for themselves
Spending money on your own account is a difficult psychological hurdle. Lots of small businesses get stuck in this chasm, happy to pitch, to network, to send out proposals and to work far into the night, but hesitate when it comes time to pay actual cash money for marketing, trade show booths or other sorts of media.
For the bootstrapper, for the woman who has worked so hard to get to postive cash flow, it feels dangerously daring, on the verge of insanity.
The question is: do successful businesses spend money on media, or does spenidng money on media make you successful?
(I think it’s some of both.)
WinZip iOS App Tops 500K Downloads In 8 Days
The WinZip iOS app, which lets you crack open .zip files from your iDevice, has only spent a short time on the App Store (and by short, I mean, like, a week). But there’s already plenty to brag about.
The app saw over 30,000 downloads on Day One, and nearly 150,000 on Day Two. As of last night, WinZip has blown by the 500,000 download hurdle with approximately 512,000 installs.
Let’s put this into some perspective, yes?
Social to-do app Any.DO saw over 500,000 downloads in its first thirty days of availability on the Android Market. Meanwhile, Kevin Rose’s micro-recommendation app Oink clocked in at 100k downloads in its first three weeks or so on iOS.
Who knew Apple loyalists needed a way to open .zip files so desperately? (Other than WinZip, of course.)
I spoke with WinZip president Patrick Nichols to see just how these crazy numbers came to be, and surprisingly there wasn’t much to it.
“It was really organic,” said Nichols. “The reality is that we just wanted to build a good application and get the word out.”
And apparently it worked: 500,000-strong on the download front, and more to come. Nichols said that an Android version of the app will be available around spring.
"If we are feeling distracted, we should pay attention to that distraction. It may be telling us that…"
“If we are feeling distracted, we should pay attention to that distraction. It may be telling us that there is something better elsewhere, something more deserving of our attention. Or it may be telling us we are on the wrong path, just when we thought we were zooming in to that perfect conclusion of a paragraph or a project. Or it may be telling us we need better tools, that the set-up we have is not fully appreciating the particularities and peccadilloes of our own work life and demands. Or it may be telling us that we need better partners, or a better method, someone or something to help us over the hurdle. Or it may just be telling us we are working too hard and we need to put down what we are doing and go outside for a walk, or stop for a cup of tea, or go for a run, or maybe just check out Facebook for a while. Distraction is our friend because it reminds us that we are fully human, not just workers, and that our lives are complex and, trying to shut out the complexity, may in fact turn out to be the least productive way to lead a life.” Cathy N. Davidson – Distraction is Our Friend In a response to Hanif Kurieshi’s wonderful The Art Of Distraction, Davidson lays down some pretty good thoughts, too.
–





