WebDriver+JavaScriptでWebアプリのE2Eテスト

Webアプリが正しく動作することを、Webブラウザーを操作して確認する E2E (End-to-End) テスト。テストの記述には様々なプログラミング言語が使えます。

Selenium WebDriver + JavaScript で E2E テストをするやり方が

An Introduction to WebDriver Using the JavaScript Bindings – Tuts+ Code Tutorial

で紹介されています。

この記事は、基本となる WebDriverJS 以外に、7つのクライアントAPIライブラリーを紹介しています。どれも github で公開されていたので、スター数を調べてみました (2014/12/07時点)。

また、テストの際に、期待通りの値になっているかどうかを調べる assert API がライブラリに組み込まれているか、それとも別途 jasmine や mocha, Q といったテストフレームワークが必要かも調べました。

Client API スター数 assert 補足
WebDriverJS N/A 別途 W3Cで標準化しているAPIで書く。JavaでSelenium動かしていた人向け。
WD.js 658 別途 builder pattern (fluent interface) で書く。
WebDriver.io 661 別途 builder patternで書く。WD.jsより短く書ける。
Testium 203 別途 CoffeeScript で書く。
Leadfoot 63 別途 Internで使われてる。WD.jsと似たAPI。
Nightwatch 2,386 組み込み 設定ファイルが必要。拡張コマンドは別ファイルに書く。
DalekJS 481 組み込み Selenium Server まで入った全部入り。Webサイトが派手w。
Webdriver-sync 36 組み込み Java APIに準拠。同期型。

さらに、WebdriverJS, WD.js, WebdriverIO, Nightwatch, Dalek の5つについて、実際にコードを書いてみました。

お題は「Googleで”webdriver”を検索した時のヒット数を標準出力に表示する」です。assert は使いません。

共通の事前準備

Mac を使います。Web ブラウザーは Chrome です。
Homebrew と Cask で node.js と java をインストールします。

$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
$ brew install node
$ brew brew install brew-cask
$ brew cask install java

さらに Selenium Server と Chrome Driver をインストールします。

$ npm install selenium-standalone chromedriver -g

コードを実行する前に、Selenium Server を起動しておきます。ただし、Dalek の場合は不要です。

$ start-selenium

WebDriverJS

APIはこちら。豊富です。
http://selenium.googlecode.com/git/docs/api/javascript/index.html

インストール

$ npm install selenium-webdriver

コード

webdriverjs.js:

var webdriver = require('selenium-webdriver');
var By = webdriver.By;

//WebブラウザーはChrome
var driver = new webdriver.Builder().
   withCapabilities(webdriver.Capabilities.chrome()).
   build();
var $ = driver.findElement.bind(driver);

//Googleを開く。
driver.get('http://www.google.com');

//検索ボックスにwebdriverと入力する。
$(By.name('q')).sendKeys('webdriver');

//検索ボタンを押す。
$(By.name('btnG')).click();

//ヒット数が表示されるまで待つ。
var timeoutMSec = 2000;
driver.wait(webdriver.until.elementLocated(By.id('resultStats')), timeoutMSec)
.then(function() { //waitした後はthenでつなぐ
    $(By.id('resultStats')).getText().then(function(text) {
        console.log(text);
    });
})
.then(function() {
    driver.quit();
});

実行

$ node webdriverjs.js 
約 615,000 件 (0.11 秒) 

WD.js

sendKeys() など WebDriverJS の名残があります。また、waitForElementByCss()など、APIが若干長めです。

インストール

$ npm install wd

コード

wd.js:

var wd = require('wd');
var browser = wd.promiseChainRemote();
var timeoutMSec = 1000;
browser
    .init({
        browserName: 'chrome'
    })
    .get('http://www.google.com')
    .elementByName('q')
    .sendKeys('webdriver')
    .elementByName('btnG')
    .click()
    .waitForElementByCss('#resultStats', timeoutMSec)
    .text(function(err, text) {
        console.log(text);
    })
    .quit();

実行

$ node wd.js
約 615,000 件 (0.12 秒)

Webdriver.io

今回試した中では、API 名が短くて好きです。

インストール

$ npm install webdriverio

コード

webdriverio.js:

var timeoutMSec = 1000;
var webdriverio = require('webdriverio')
    .remote({
        desiredCapabilities: {
            browserName: 'chrome'
        }
    })
    .init()
    .url('http://www.google.com')
    .setValue('[name="q"]', 'webdriver')
    .click('[name="btnG"]')
    .waitFor('#resultStats', timeoutMSec)
    .getText('#resultStats', function(err, text) {
        console.log(text);
    })
    .end();

実行

$ node webdriverio.js 
約 615,000 件 (0.12 秒) 

Nightwatch

runner を使って実行するので、出力が綺麗です。
デフォルトのブラウザーが Firefox で、Chrome を使うためには設定ファイルが必要です。
拡張コマンドを置くためのディレクトリが必要です。

インストール

$ npm install nightwatch -g
$ mkdir -p examples/custom-commands

コード

nightwatch.json:

{
  "selenium": {
    "cli_args": {
      "webdriver.chrome.driver": "/usr/local/bin/chromedriver"
    }
  },
  "test_settings" : {
    "default" : {
      "silent": true,
      "desiredCapabilities": {
        "browserName": "chrome"
      }
    }
  }
}

nightwatch.js:

module.exports = {
  "webdriverの検索ヒット数を表示する" : function (browser) {
    var timeoutMSec = 1000;
    browser
      .url("http://www.google.com")
      .setValue('[name="q"]', 'webdriver')
      .click('[name="btnK"]') // FirefoxとChromeでは表示されるボタン名が違います。
      .waitForElementPresent('#resultStats', timeoutMSec)
      .getText('#resultStats', function(res) {
          console.log(res.value);
      })
      .end();
  }
};

実行

$ nightwatch -t nightwatch.js 

[Nightwatch] Test Suite
=======================

Running:  webdriverの検索ヒット数を表示する 

✔  Element <#resultStats> was present after 1038 milliseconds.
約 615,000 件 (0.14 秒) 

OK. 1 total assertions passed. (11.812s)

DalekJS

Selenium server を動かす必要はありません。

インストール

$npm install dalek-cli -g
$npm install dalekjs dalek-browser-chrome --save-dev

コード

waitForElementが動作しないので、waitForを使いました(github)。
標準出力への表示はexecute()とlog.message()を組み合わせて使います。

dalek.js:

module.exports = {
  'webdriverのヒット数を表示する': function (test) {
    var timeoutMSec = 10000;
    test
      .open("http://www.google.com")
      .setValue('[name="q"]', 'webdriver')
      .click('[name="btnG"]')
      //.waitForElement('#resultStats', timeoutMSec)
      .waitFor(function() {
        return Boolean(document.querySelector('#resultStats'));
      }, [], timeoutMSec)
      .execute(function() {
        var result = document.querySelector('#resultStats').innerText;
        this.data('result', result);
      })
      .log.message(function() {
        return test.data('result');
      })
      .done();
  }
};

実行

引数 -b chrome を与えて Chrome で動かします。
デフォルトではヘッドレスブラウザーの PhantomJS で動かします。

$ dalek dalek.js -b chrome
Running tests
Running Browser: Google Chrome
OS: Mac OS X 10.10.1 x86_64
Browser Version: 39.0.2171.71

RUNNING TEST - "webdriverのヒット数を表示する"
▶ OPEN http://www.google.com
▶ SETVALUE [name="q"]
▶ CLICK [name="btnG"]
▶ WAITFOR 
▶ EXECUTE 
☁ [USER] MESSAGE: 約 615,000 件 (0.38 秒) 
✔ 0 Assertions run
✔ TEST - "webdriverのヒット数を表示する" SUCCEEDED

 0/0 assertions passed. Elapsed Time: 4.44 sec 

どれを選ぶか

短い API が好きなので、Webdriver.ioNightwatchですね。すでにテストフレームワークをチームに導入しているかどうかで決める。

(ただ、Nightwatch の対抗馬としてInternも気になります。もうちょっと調べてみよう)

ブックマークレットをChrome拡張機能から実行する方法

ブックマークレットは便利だけれど、インストールが大変。そこで、Chrome拡張機能として作り直してみた。結果的には、2つのファイルを追加すれば実現できた。

「ボタンを押すとスクリプトを実行する」という動作にしたかったので、Page Action として作った。スクリプトを動作させたいページを開くと、アドレスバーの横にアイコンを表示する。そのアイコンをクリックすると、スクリプトを実行する。

(1) manifest.json を作る
ポイントは、permissions に、”tabs”と、スクリプトを注入するページのURLを記載する点。
URLはアスタリスク (*) を使ったパターンを記述することもできる。詳しくは Match Patterns を参照。


{
"manifest_version": 2,
"name": "Name of this extension",
"version": "0.1", // 新しい拡張を公開するたびに上げていく
"description": "Description of this extension",
"icons": {
"128": "icon128.png" // chrome://extensions に表示されるアイコン。128×128
},
"page_action": {
"default_icon": "icon128.png", // アドレスバーに表示されるアイコン。19×19, 38×38 を準備しておくべき?
"default_title": "Tooltip for the page action icon"
},
"permissions": [
"tabs",
"https://www.google.com/maps/preview" // スクリプトを実行したいURLを記載していく
],
"background": {
"scripts": ["background.js"]
}
}

view raw

manifest.json

hosted with ❤ by GitHub

(2) background.js を作る
ポイントは2つ。
1. Page Action のアイコンを表示したいURLの正規表現を記述する。正規表現と、上記 (1) のアスタリスク記法は異なるので注意。
2. 実行したいスクリプト (ブックマークレット) のファイル名、ここでは execute.js を記述する。別ファイルにせずに、直接記述することもできる (file ではなく code にする)。


var urlRegExp = new RegExp('^https?://www.google.com/maps/preview'); // アドレスバーの横にアイコンを表示するときのURL正規表現
function showIcon(tabId, changeInfo, tab) {
if (tab.url.match(urlRegExp)) {
chrome.pageAction.show(tabId);
}
}
chrome.tabs.onUpdated.addListener(showIcon);
chrome.pageAction.onClicked.addListener(function(tab) {
chrome.tabs.executeScript(tab.id, {
"file": "execute.js" //実行したいスクリプトのファイル名 (execute.js)
}, function () {
if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError.message);
}
});
});

view raw

background.js

hosted with ❤ by GitHub

ここまで準備ができれば、Chrome単体で拡張機能を作って試すことができる。ただ、簡単にインストールできるようにするためには、Chromeウェブストアで公開する必要がある。

(3) Chromeウェブストアで公開する
まだ公開していないので、できたら報告します。
ダッシュボードにアクセスして、拡張機能を登録。ポイントは2つ。
1. 開発者登録に $5 必要。一度払えば、ずっと使えるらしい。
2. ローカルのChromeで作った.crxではなく、素の zip で固めた .zip ファイルを登録する。Chromeウェブストア側で秘密鍵を発行して署名してくれるらしい。

参考:

Cent OS 5.9 に rbenv で Ruby, bundler をインストール

Cent OS 5.9 に rbenv を使って Ruby と bundler をインストールした。

(1) git のインストール
$ sudo yum install git
$ git –version
git version 1.8.2.1

(2) rbenv, ruby-build のインストール
$ git clone git://github.com/sstephenson/rbenv.git ~/.rbenv

rbenv パスを bash に教えるため、以下の2行を追加
$ vi ~/.bashrc
export PATH=”$HOME/.rbenv/bin:$PATH”
eval “$(rbenv init -)”

.bashrc を反映
$ source ~/.bashrc

ruby-build をインストール
$ git clone git://github.com/sstephenson/ruby-build.git
$ cd ruby-build
$ sudo ./install.sh

(3) ruby のインストール
$ sudo yum install -y readline readline-devel zlib zlib-devel openssl-devel g++ gcc-c++

インストール可能なバージョン一覧を表示
$ rbenv install -l

バージョンを指定してインストール
$ rbenv install 1.9.3-p448

デフォルトで利用するバージョンを指定
$ rbenv global 1.9.3-p448
$ rbenv rehash
$ ruby -v
1.9.3-p448

(4) bundler のインストール
$ rbenv exec gem install bundler

bundler を実行するときは rbenv exec bundle として実行
$ rbenv exec bundle -v
Bundler version 1.3.5

これで rails とか入れたらいい。

参考
CentOS 5.8 に ruby 1.9.3 と Rails 3.3.2 をインストール
http://krakenbeal.blogspot.jp/2012/03/centos-58-ruby-193-rails-332.html

Rails開発環境の構築(rbenvでRuby導入からBundler、Rails導入まで) – Qiita [キータ]
http://qiita.com/emadurandal/items/a60886152a4c99ce1017

Cent OS 5.9 に yum を使って Python 2.6 をインストール

Cent OS 5.9 に Python 2.6 をインストールして、easy_install-2.6 と pip を使えるようにした。
楽するポイントは、EPEL パッケージから yum でインストールする点(EPEL使うと、標準パッケージでは発生しない問題が起きるかもしれません。個人の責任でご利用ください)。

(1) EPEL パッケージを導入

自分が使っている OS を調べる。

$ cat /etc/redhat-release
CentOS release 5.9 (Final)

$ uname -a
Linux bstakatama01 2.6.18-274.18.1.el5 #1 SMP Thu Feb 9 12:45:44 EST 2012 x86_64 x86_64 x86_64 GNU/Linux

CentOS 5.x で 64 bit なことが分かったので、CentOS 外部レポジトリの追加(EPEL)を参考に、rpm を wget し rpm をインストール。

$ wget http://ftp-srv2.kddilabs.jp/Linux/distributions/fedora/epel/5/x86_64/epel-release-5-4.noarch.rpm
$ sudo rpm -ivh epel-release-5-4.noarch.rpm

(2) Python2.6, easy_install-2.6 を yum でインストール

EPEL パッケージから Python26 と、python26-distribute をインストール。*-distribute を入れると、easy_install-2.6 が使えるようになる。

$ yum install python26 python26-distribute --enablerepo=epel
$ which easy_install-2.6
/usr/bin/easy_install-2.6

(3) pip をインストール

easy_install-2.6 で pip をインストール

$ sudo easy_install-2.6 pip

これで、easy_install-2.6 や pip を使ってパッケージをインストールできるようになる。

参考

CentOS 外部レポジトリの追加(EPEL)
http://www.tooyama.org/yum-addrepo-epel.html

jsdo.itでenchant.jsを動かす

enchant.jsを使ったゲーム作り実習をやったので、その時のメモを公開。
jsdo.it で作ると、スマフォで動かすのが楽。
ちなみに私はスマフォ用に「シュシュっと手裏剣」を作りました。

(1) jsdo.it でアカウントを作る
http://jsdo.it/ で好きなサービスを使ってログイン。アカウント名はログイン後に決める。

(2) Hello World
画面に文字を表示しよう! by shi3z – code.9leap.net
http://code.9leap.net/codes/show/203
をコピペして動かしてみる。

手順:
1. Start coding を押下。
2. JavaScript タブを選択。
3. + Add Library を押下。
Major Library から enchant.js v0.6.2 – js を選択し、Add ボタンを押下。
4. 9leapのサンプルから main.js をコピペ。
5. Save ボタンを押下。実行結果を確認。

もし正しく動作しない場合は、JavaScript コンソールを確認。

(3) Start 画面追加
jsdo.it 上の別コードをインポートして、Start 画面を表示してみる。

手順:
1. JavaScript タブを選択。
2. + Add Library を押下。
Input URL に 9leap / nineleap.enchant を入力し、Add ボタンを押下。
3. ソースコードを修正。
// game = new Game();
game = new enchant.nineleap.Game();

(4) 画像を表示
スプライトを表示しよう by shi3z – code.9leap.net
http://code.9leap.net/codes/show/202
をコピペ+修正して動かしてみる。画像ファイルのURLを指定する必要がある。
使える素材は
Image Materials | enchant.js – A simple JavaScript framework for creating games and apps.
http://enchantjs.com/ja/image-materials/
を参照。商用利用でなければ、画像を改変して使うのも自由 (by @enchantjs_jaさん)。

手順:
1.
// var game = new Game(320, 320);
var game = new enchant.nineleap.Game(320, 320);

2.
// game.preload(‘chara1.gif’); // chara1.gifを読み込む
var chara1 = ‘http://enchantjs.com/images/materials/chara1.png’;
game.preload(chara1); // chara1.gifを読み込む

3.
// bear.image = game.assets[‘chara1.gif’]; // chara1.gifの中にある
bear.image = game.assets[chara1]; // chara1.gifの中にある

(5) 操作パッド
十字キーは Pad, アナログの操作キーは APad。
ソースコードは http://jsdo.it/takatama/Tn9F/ を参照のこと。

手順:
1. JavaScript タブを選択。
2. + Add Library を押下。
Input URL に 9leap / ui.enchant と入力し Add ボタンを押下。
3. ソースコードを入力。

var pad = new enchant.ui.Pad();
pad.moveTo(10, 220);
game.rootScene.addChild(pad);

bear.addEventListener('enterframe', function () {
    var input = game.input;
    //キーに応じて移動
    if (input.left) {
        this.x -= 4;
    }
    if (input.right) {
        this.x += 4;
    }
    if (input.up) {
        this.y -= 4;
    }
    if (input.down) {
        this.y += 4;
    }
    if (game.frame %4 === 0) {
        this.frame = 2- this.frame;
    }
});

var apad = new enchant.ui.APad();
apad.moveTo(120, 220);
game.rootScene.addChild(apad);

bear.addEventListener('enterframe', function () {
    if (apad.isTouched) {
        this.x += apad.vx*4;
        this.y += apad.vy*4;
    }
});

(6) スマフォで操作する
URLを jsdo.it から jsrun.it に変更して、スマフォのブラウザーからアクセス。
例えば http://jsdo.it/takatama/Tn9F/ なら http://jsrun.it/takatama/Tn9F/ にすればよい。

(7) End画面を追加
上記(3)でStart画面を追加済みなら、ゲーム終了時に、game.end(score, message); を呼び出せば良い。

(8) Clear画面を追加
ちょっとした工夫が必要。Gameをnewする前後にソースコードを追記する。

enchant.nineleap.assets.push('http://wise9.github.com/enchant.js/images/clear.png');
var game = new enchant.nineleap.Game();
game.clear = function(score, message) {
    this.endScene = new SplashScene();
    this.endScene.image = this.assets['http://wise9.github.com/enchant.js/images/clear.png'];
    this.end(score, message);
};

青空文庫をKindleで読むためのRubyスクリプト

青空文庫を Kindle で読むには「Kindleストア>本>無料本」で探すのが一番簡単。Kindle ストアで見つからなければ、手元の Mac で mobi ファイルを作る。下準備に手間がかかるけれど、新着の小説をいち早く Kindle で読めるようになる。

動作環境:

$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.7.5
BuildVersion: 11G63

手順

0. 前準備(ツールの入手)
1. 青空文庫からXHTMLを入手
2. mobiに変換し確認

0. 前準備(ツールの入手)

(1) Rubyスクリプト aozora2kindle.rb を github からインストール

$git clone https://github.com/takatama/aozora2mobi-ruby.git

(2) RubyでXHTMLを操作するためにHTMLパーサー nokogiri をインストール

Nokogiri
http://nokogiri.org/tutorials/installing_nokogiri.html

の手順に従うんだけれど、少々面倒。

(3) KindleGenと、Kindleプレビューツールを以下からインストール。いずれも無料。

Amazon.co.jp:Kindle ダイレクト・パブリッシング:ヘルプ
https://kdp.amazon.co.jp/self-publishing/help?topicId=A3IWA2TQYMZ5J6

KindleGenは、XHTMLファイルからmobiファイルを作るコマンドラインツール。zipを解凍したディレクトリを適切な場所に移動してから、

$cd aozora2kindle-ruby
$ln -s (解凍したディレクトリ)/kindlegen .

としておくと便利。

Kindleプレビューツールはできたmobiファイルを確認するためのGUI。「アプリケーション」フォルダに移動しておく。

1. 青空文庫からXHTMLを入手

青空文庫のXHTMLへのURLを探す。例えば、坊ちゃんの場合は、次のURLになる。

http://www.aozora.gr.jp/cards/000148/files/752_14964.html

このURLをaozora2kindle.rbに渡して実行。

$cd aozora2kindle-ruby
$ruby aozora2kindle.rb http://www.aozora.gr.jp/cards/000148/files/752_14964.html

すると、752_14964.htmlが作成される。あわせて、mobi ファイルに変換するための下準備として、(1) XHTMLファイルが参照するCSSファイルの入手、(2) CSSファイルが参照する画像の入手、(3) CSSファイルでの縦書き指定、(4) XHTMLファイルが参照する画像の入手、をそれぞれ実施している。

2. mobiに変換し確認

KindleGenを使って、下ごしらえしたXHTMLファイルをmobiファイルに変換する。

$./kindlegen 752_14964.html

Kindleプレビューツールを使って、できたmobiファイルを確認する。mobiファイルをKindleに送信すれば持ち運べるようになる。

Google Analyticsを使ったコホート分析で施策の有効性を検証する

サービスを成長させるためには、機能追加や削除、デザイン変更、広告など、様々な施策によって新たなユーザーを集客する必要がある。ある施策によって集客したユーザーの集団(コホート)の動向を時系列で分析する「コホート分析」により、その施策の有効性を検証することができる。

例えば、獲得ユーザーを月ごとに分け、再訪問者数をプロットしている(How to do Cohort Analysis in Google Analyticsより引用)。獲得した次の月になると、再訪問者は大幅に下がる様子がわかる。9月と11月の獲得ユーザーで比べると、11月の方が月を再訪問者数を保持できており、改善効果が見て取れる。

以下では、Google Analyticsを使い「初訪問した時期」でコホート分析するための具体的な手続きを紹介する。

(1) トラッキングコードの設置

Google Analyticsのトラッキングコードが以下だとして、

<script type="mce-text/javascript">
    var _gaq = _gaq || [];
    _gaq.push(['_setAccount', 'UA-XXXXXXXX-X']);
    _gaq.push(['_trackPageview']);
    (function() {
        var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
        ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
        var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
     })();
</script>

次のようなコードを追加して、ユーザーの初訪問日をyyyymmdd形式でカスタム変数に保存する。具体的には、2013年3月28日に初訪問していたユーザーは、カスタム変数名「FirstVisit」に、値「20130328」を保存する。

<script type="mce-text/javascript">
    var _gaq = _gaq || [];

    try {
        var f = function (d) {
            return d.getFullYear() + ("0" + (d.getMonth() + 1)).slice(-2) + ("0" + d.getDate()).slice(-2);
        };
        var c = document.cookie;
        var i = c.indexOf('__utma');
        var s = null;
        if (i < 0) {
            s = f(new Date());
        } else {
            var utma = c.substring(i, c.indexOf(';', i));
            if (utma) {
                var v = utma.split('.');
                if (v[2]) {
		    var date = new Date();
                    date.setTime(v[2] * 1000);
                    s = f(date);
                }
            }
        }
        if(s) {
            _gaq.push(['_setCustomVar', 1, 'FirstVisit', s, 1]);
        }
    } catch (e) {
    }

    _gaq.push(['_setAccount', 'UA-XXXXXXXX-X']);
    _gaq.push(['_trackPageview']);
    (function() {
        var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
        ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
        var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
     })();
</script>

trackPageviewを実行するより前に、setCustomVarを実行する点に注意する(Recommended Practiceを参照のこと)。

(2) アドバンスセグメントを使った分析

Google Analyticsの左側ペインで、ユーザー>カスタム>カスタム変数、を選択し、カスタム変数 FirstVisit が取得できていることを確認する。もし、取得できていない場合は、Google Analyticsで閲覧している日付に間違いがないか、埋め込んだトラッキングコードに間違いがないか(正しく実行できていれば、__utmvというクッキーが作成されている)などを見直す。

スクリーンショット 2013-03-28 17.00.43

以下、「2013年3月に初訪問した人」の集団について分析するため、「FirstVisit の値が 201303 になっている人」を抽出するカスタムセグメントを作る。

右側ペインの上部の「アドバンスセグメント」を選択し、右下の「新しいカスタムセグメント」を押下する。

スクリーンショット 2013-03-30 1.49.18

下図のように「カスタム変数(キー1)」は「FirstVisit」と「完全一致」、さらに、「カスタム変数(値01)」は、「201303」と「前方一致」させる。

スクリーンショット 2013-03-30 1.50.34

これで、2013年3月に初訪問した人たちが、サイトにどれだけ再訪問し、どう利用しているかを把握できるようになる。

スクリーンショット 2013-03-30 1.58.36

参考にしたサイト:

アクセス分析の有意性を高めるためにKISSmetricsがコホート分析を導入 | TechCrunch Japan
http://jp.techcrunch.com/2011/09/24/20110923kissmetrics-helps-you-hone-in-on-stats-that-actually-matter-with-cohort-reports/

How to do Cohort Analysis in Google Analytics | Jonathon Balogh
http://jonathonbalogh.com/2012/04/01/how-to-do-cohort-analysis-in-google-analytics/

How to do Cohort Analysis in Google Analytics | Jonathon Balogh
http://jonathonbalogh.com/2012/04/01/how-to-do-cohort-analysis-in-google-analytics/

Google Analyticsで、新規訪問の日を記録するには? – QA@IT
http://qa.atmarkit.co.jp/q/2274

Custom Variables – Web Tracking (ga.js) – Google Analytics — Google Developers
https://developers.google.com/analytics/devguides/collection/gajs/gaTrackingCustomVariables

カスタム変数を作るコードは、Gistにもあげておいた。

try {
var f = function (d) {
return d.getFullYear() + ("0" + (d.getMonth() + 1)).slice(-2) + ("0" + d.getDate()).slice(-2);
};
var c = document.cookie;
var i = c.indexOf('__utma');
var s = null;
if (i < 0) {
s = f(new Date());
} else {
var utma = c.substring(i, c.indexOf(';', i));
if (utma) {
var v = utma.split('.');
if (v[2]) {
var date = new Date();
date.setTime(v[2] * 1000);
s = f(date);
}
}
}
if(s) {
_gaq.push(['_setCustomVar', 1, 'FirstVisit', s, 1]);
}
} catch (e) {
}
view raw ga-cohort.js hosted with ❤ by GitHub

jsdo.itでProcessingを動かす

メディアアートのためのプログラミング言語ProcessingProcessing.jsを使うことで、作った作品をJavaScriptのコミュニティサイトjsdo.itに公開することができる。

jsdo.itでの手順は次の通り。

(1) jsdo.itで新しいコードを書き始める(Start Coding)

スクリーンショット 2013-03-21 13.14.10

(2) JavaScriptのライブラリ追加(Add Library)

スクリーンショット 2013-03-21 13.16.46

(3) Processing.js v1.4.0 – js を選択して Add

(4) HTMLタブに以下のテンプレートを記述

<script type="application/processing" data-processing-target="pjs">
//Processing code is here
</script>
<canvas id="pjs"> </canvas>

(5) //Processing code is here のところに、Processingのコードを記述

このやり方では、JavaScriptタブにはコードを書かない。

Built with Processing[Ver. 1.x対応版] -デザイン/アートのためのプログラミング入門 3-6-4 力をためる、をベースに作ったコードを jsdo.it で公開してみた。

力をためる – jsdo.it
スクリーンショット 2013-03-21 13.03.26

Built with Processingはとても丁寧な解説で、プログラミング自体の入門書としてもオススメ。小学校4年生の娘もこの本(の前半)を使って自習していて、思い通りに動いたときには喜びに身悶えしてる。

JavaScript Development Tools – JavaScript開発の効率アップ

id:Layzie さんの発表「JavaScript Development Tools – JavaScript開発の効率アップ」がためになったので、プレイリストを作ってみました。(xx:xx) をクリックすると、該当する動画に飛びます。

Layzieさんによる発表への補足記事はこちらです。

expressで作ったHelloWorldをsupertestでテストする

node.jsのためのWebアプリケーションフレームワークexpress。主にURLルーティングのテストには supertest が便利。

(1) expressでHelloWorld
express の Getting started に従って、HelloWorldを表示するWebアプリを作ってみる。

$npm install express
$vi app.js

var express = require('express'),
    app = express();

app.get('/hello.txt', function (req, res) {
    res.send('Hello World');
}); 
app.listen(3000, '0.0.0.0');

$node app.js

app#send を使うと、Content-Length を自動的に追加してくれる。
ブラウザから http://localhost:3000/hello.txt にアクセスすると、Hello World と表示される。

(2) mocha と supertest でテストを書く
mocha は単体テストのためのフレームワーク。supertest と組み合わせて使うことで、express で作った Web アプリの動作を、HTTPリクエストレベルで確認できる。

はじめに、単体テストができるように app.js を修正しておく。

var express = require('express'),
    app = express();

app.get('/hello.txt', function (req, res) {
    res.send('Hello World');
}); 

module.exports = app;

if (!module.parent) {
    app.listen(3000, '0.0.0.0');
}

8行目で単体テスト側からWebアプリのロジックにアクセスできるよう、エクスポートしている。
10行目は単体テスト経由でWebアプリを動作させている場合には listen させないようにしている(単体テスト経由の場合 module.parent に値が入る)。

次に単体テスト側を準備する。
mocha はインストール済み($npm install mocha)だと手順は次の通り。

$npm install supertest
$mkdir test
$vi test/test_app.js

var request = require('supertest');
var app = require('../app');

describe('helloworld', function () {
    it('should return 200 with Content-Length', function () {
        var expected_body = 'Hello World';
        request(app)
            .get('/hello.txt')
            .expect('Content-Length', expected_body.length)
            .expect(200)
            .end(function (err, res) {
                if (err) {
                    throw err;
                }
            });
    });
});

$node_modules/mocha/bin/mocha --reporter spec

今回は実装を作ってからテストしたけれど、テスト駆動開発にも役立ちそう。