技術ブログ - 毎日が成長!

2010年12月 のアーカイブ

JasmineによるJavaScriptのテスト その4

2010年12月24日 by ogura

こんにちは、プログラマのおぐらです。毎度おなじみ流浪のJasmineチュートリアルをお送りします。

前回のJasmineによるJavaScriptのテスト その3では、beforeEachafterEachによる「テストの事前準備と後始末」について解説しました。今回は実際にテストを記述する部分を詳細に説明していきます。

テストコード

Jasmineでは、テストコードがそのまま英文として読めるように設計されています(これはビヘイビア駆動開発の特徴でもあります)。

expectメソッドとMatcherメソッド

xUnit系のテスティングフレームワークにおけるassert系メソッドに相当するものが、expectメソッドとMatcherメソッドです。

テスト対象であるvalueが期待値である「5」と同一であることを検証する場合、xUnit系では、

assertEquals(5, value);

のように書きますが、Jasmineでは、

expect(value).toEqual(5);

のように書きます。

このように、Jasmineのテストコードは「expectメソッドが返すオブジェクトのtoXXXXXメソッドで、テスト対象が期待値どおりであるかどうかを評価する」という構造になっており、toXXXXXのような評価用のメソッドをMatcherと呼びます。

定義済みのMatcher

Jasmineでは以下のMatcherが定義されており、さらに自分で定義することもできます。

Matcher 意味
expect(a).toEqual(b) abと同値であることを期待する
expect(a).toBe(b) abと同一オブジェクトであることを期待する
expext(a).toBeDefined() aが定義されていることを期待する(undefinedでない)
expect(a).toBeNull() anullであることを期待する
expect(a).toBeTruthy() atrueであることを期待する
expect(a).toBeFalsy() afalseであることを期待する
expect(a).toContain(b) abが含まれていることを期待する
expect(a).toBeLessThan(b) abより小さいことを期待する
expect(a).toBeGreaterThan(b) abより大きいことを期待する
expect(fn).toThrow(e) fnが例外をスローすることを期待する

上記の一覧にはa != bのように否定形をテストするものがありませんが、Jasmineではexpect(a).toEqual(b)の否定形は、

expect(a).not.toEqual(b);

のように、expectメソッドとMatcherメソッドの間にnotをはさむことで表現します。

DOM操作を伴うテストの書き方

JasmineにはDOM操作を行う機能が提供されていません。そのため、テスト用のDOM要素は自分で用意する必要があります。

テストの準備

お馴染みのdocument.getElementByIdメソッドのテストを書いてみましょう。まず、新しくプロジェクトディレクトリにspec/DocumentSpec.jsを作成します。

このテストでは「getElementByIdでIDがtest-nodeのDOM要素を取得し、その結果がnullではないこと」をテストしています。

次に、このspec/DocumentSpec.jsを読み込むためにSpecRunner.htmlを以下のように変更します(第2回の「最初のテスト」で作成したSpecRunner.htmlの12行目にあるspec/ArraySpec.jsspec/DocumentSpec.jsに変更しただけです)。

SpecRunner.htmlを編集してテストファイルを読み込む」という作業はテストを追加した際に必ず必要となりますが、以降は記載を省略します。新しいテストファイルを追加した際は、適時SpecRunner.htmlを編集するようにしてください。

テストの実行

それではテストを実行してみましょう。ブラウザでSpecRunner.htmlを開いてみます。

テスト結果

テストは失敗となりました。

このテストでは、

var elementId = 'test-node';
expect(document.getElementById(elementId)).not.toBeNull();

という箇所で「DOMツリーにtest-nodeというIDを持ったDOMノードがあること」を期待しています。しかし、test-nodeというIDを持つノードは存在していないため、document.getElementById('test-node')nullを返し、その結果テストが失敗したわけです。

SpecRunner.htmlbody要素内に<div id="test-node"></div>という記述を追加すればテストは成功しますが、そうするとすべてのテストがSpecRunner.htmlの内容に依存してしまいます。これを回避するには、beforeEachafterEachを使ってテスト時に動的にDOMツリーを操作する必要があります。

動的にDOMツリーを操作

DocumentSpec.jsを以下のように編集し、beforeEachメソッドとafterEachメソッドを追加します。

beforeEachでは、documentオブジェクトのcreateElementappendChildを利用してbody要素にテスト用のdiv要素を新しく追加しています。また、追加した要素をafterEachメソッドから参照できるよう、containerという変数に格納しています。

afterEachでは、後始末としてテスト用に追加したDOM要素をdocument.bodyから削除しています。

では、再度テストを実行してみましょう。

テスト結果

無事にテストが成功しました。

jasmine-domによるDOM構造の準備

jasmine-domを導入すると、先程のテストを以下のように簡潔に書くことができます。

だいぶスマートに記述できました。

Jasmineには、他にもjQuery用の拡張やiPhone用の拡張などがあり、Related Projectsにてアドオンの一覧が紹介されているので、参考にしてみてください。

今回はここまで。次回は最終回として「モック、スタブを使用したテストの書き方」について解説したいと思います。

 

Amazon EC2上にCentOS4系のAMIを作成する手順

2010年12月16日 by yanase

こんにちは!サーバーワークスでインフラを担当しています、柳瀬です。

先日CentOS4系のインスタンスをAWS上で動かしたいとう依頼を受けてEC2のインスタンスを作成した事がありました。 基本的な手順はCentOS5系の場合と同じですが、今回のエントリではその作成手順を記載したいと思います。 AmazonEC2のシンガポールリージョンにEC2インスタンスを起動し、CentOS4系のAMIを作成しました。

CentOS4系のインスタンスには以下のソフトウェアをインストールしておいて下さい。

  • ec2-api-tools
  • ec2-ami-tools
  • ec2-metadata

大まかな作業手順は以下の通りです。

  • 作業環境の準備、CentOS4イメージ作成
  • EBSの準備、CentOS4イメージの流し込み
  • SnapShotからAMIの作成
  • インスタンスのテスト起動

作業環境での準備、CentOS4系イメージ作成

AMI作成の作業ディレクトリを作成します

$ sudo mkdir /mnt/work
$ cd /mnt/work

CentOS4をインストールするディスクイメージを作成

$ sudo df -h
Filesystem          サイズ  使用  残り 使用% マウント位置
/dev/sda1              30G  1.5G   27G   6% /
/dev/sdc              147G  188M  140G   1% /mnt
none                  851M     0  851M   0% /dev/shm

$ sudo dd if=/dev/zero of=centos4.img bs=1M count=10240
10240+0 records in
10240+0 records out
10737418240 bytes (11 GB) copied, 298.681 seconds, 35.9 MB/s

$ ls -alh
合計 11G
drwxr-xr-x 2 root root 4.0K  8月  2 16:57 .
drwxr-xr-x 4 root root 4.0K  8月  2 16:55 ..
-rw-r--r-- 1 root root  10G  8月  2 17:02 centos4.img

# df -h
Filesystem          サイズ  使用  残り 使用% マウント位置
/dev/sda1              30G  1.5G   27G   6% /
/dev/sdc              147G   11G  130G   8% /mnt
none                  851M     0  851M   0% /dev/shm

作成したディスクイメージにファイルシステムを作成

$ sudo /sbin/mke2fs -F -j centos4.img
mke2fs 1.39 (29-May-2006)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
1310720 inodes, 2621440 blocks
131072 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=2684354560
80 block groups
32768 blocks per group, 32768 fragments per group
16384 inodes per group
Superblock backups stored on blocks:
        32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632

Writing inode tables: done
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done

This filesystem will be automatically checked every 25 mounts or
180 days, whichever comes first.  Use tune2fs -c or -i to override.

CentOSをインストールする準備をします

$ sudo mkdir /mnt/ec2-fs
$ sudo /bin/mount -o loop centos4.img /mnt/ec2-fs
$ df -h
Filesystem          サイズ  使用  残り 使用% マウント位置
/dev/sda1              30G  1.5G   27G   6% /
/dev/sdc              147G   11G  130G   8% /mnt
none                  851M     0  851M   0% /dev/shm
/mnt/work/centos4.img
                      9.9G  151M  9.2G   2% /mnt/ec2-fs

$ sudo /bin/mkdir /mnt/ec2-fs/dev

$ for i in console null zero ; do sudo /sbin/MAKEDEV -d /mnt/ec2-fs/dev -x $i; done

$ sudo mkdir /mnt/ec2-fs/etc/

$ sudo vi /mnt/ec2-fs/etc/fstab
      1 /dev/sda1  /         ext3    defaults        1 1
      2 none       /dev/pts  devpts  gid=5,mode=620  0 0
      3 none       /dev/shm  tmpfs   defaults        0 0
      4 none       /proc     proc    defaults        0 0
      5 none       /sys      sysfs   defaults        0 0

$ sudo vi yum-xen.conf
      1 [main]
      2 cachedir=/var/cache/yum
      3 debuglevel=2
      4 logfile=/var/log/yum.log
      5 exclude=*-debuginfo
      6 gpgcheck=0
      7 obsoletes=1
      8 reposdir=/dev/null
      9
     10 [base]
     11 name=CentOS-4.8 - Base
     12 mirrorlist=http://mirrorlist.centos.org/?release=4.8&arch=i386&repo=os
     13 enabled=1
     14
     15 [updates-released]
     16 name=CentOS-$releasever - Updates
     17 mirrorlist=http://mirrorlist.centos.org/?release=4.8&arch=i386&repo=updates
     18 enabled=1

$ sudo mkdir /mnt/ec2-fs/proc
$ sudo mount -t proc none /mnt/ec2-fs/proc
$ df -h
Filesystem          サイズ  使用  残り 使用% マウント位置
/dev/sda1              30G  1.5G   27G   6% /
/dev/sdc              147G   11G  130G   8% /mnt
none                  851M     0  851M   0% /dev/shm
/mnt/work/centos4.img
                      9.9G  151M  9.2G   2% /mnt/ec2-fs

CentOS4をインストールします

$ sudo yum -c yum-xen.conf --installroot=/mnt/ec2-fs -y groupinstall Core
$ sudo yum -c yum-xen.conf --installroot=/mnt/ec2-fs -y groupinstall Base
$ sudo yum -c yum-xen.conf --installroot=/mnt/ec2-fs -y clean all
$ sudo yum -c yum-xen.conf --installroot=/mnt/ec2-fs -y install curl ruby

各種設定をします

$ sudo vi /mnt/ec2-fs/etc/sysconfig/network-scripts/ifcfg-eth0
      1 DEVICE=eth0
      2 BOOTPROTO=dhcp
      3 ONBOOT=yes
      4 TYPE=Ethernet
      5 USERCTL=yes
      6 PEERDNS=yes
      7 IPV6INIT=no

$ sudo vi /mnt/ec2-fs/etc/sysconfig/network
      1 NETWORKING=yes

$ sudo vi /mnt/ec2-fs/etc/fstab
      1 /dev/sda1  /         ext3    defaults        1 1
      2 /dev/sda2  /mnt      ext3    defaults        0 0
      3 /dev/sda3  swap      swap    defaults        0 0
      4 none       /dev/pts  devpts  gid=5,mode=620  0 0
      5 none       /dev/shm  tmpfs   defaults        0 0
      6 none       /proc     proc    defaults        0 0
      7 none       /sys      sysfs   defaults        0 0

EC2上でインスタンスを起動する際に実行させるスクリプトを配置します。

ここがCentOS4系で作成する場合のポイントとなります。CentOS4系のcurlはバージョンが7.12.1です。

EC2のインスタンスは起動時にAmazonが提供しているssh公開鍵を設置するスクリプトを実行する必要があります。

しかし、スクリプト内で使用されているcurlのオプションが7.12.1で使用できません。

今回は使用出来なかった“retry”と、“retry-delay”オプションを削除して以下のように修正しています。

$ sudo vi /mnt/ec2-fs/usr/local/sbin/get-credentials.sh
     1  #!/bin/bash
     2
     3  # Retreive the credentials from relevant sources.
     4
     5  # Fetch any credentials presented at launch time and add them to
     6  # root's public keys
     7
     8  PUB_KEY_URI=http://169.254.169.254/1.0/meta-data/public-keys/0/openssh-key
     9  PUB_KEY_FROM_HTTP=/tmp/openssh_id.pub
    10  PUB_KEY_FROM_EPHEMERAL=/mnt/openssh_id.pub
    11  ROOT_AUTHORIZED_KEYS=/root/.ssh/authorized_keys
    12
    13  # We need somewhere to put the keys.
    14  if [ ! -d /root/.ssh ] ; then
    15          mkdir -p /root/.ssh
    16          chmod 700 /root/.ssh
    17  fi
    18
    19  # Fetch credentials...
    20
    21  # First try http
    22  curl --silent --fail -o $PUB_KEY_FROM_HTTP $PUB_KEY_URI
    23  if [ $? -eq 0 -a -e $PUB_KEY_FROM_HTTP ] ; then
    24      if ! grep -q -f $PUB_KEY_FROM_HTTP $ROOT_AUTHORIZED_KEYS
    25      then
    26              cat $PUB_KEY_FROM_HTTP >> $ROOT_AUTHORIZED_KEYS
    27              echo "New key added to authrozied keys file from parameters"|logger -t "ec2"
    28      fi
    29      chmod 600 $ROOT_AUTHORIZED_KEYS
    30      rm -f $PUB_KEY_FROM_HTTP
    31
    32  elif [ -e $PUB_KEY_FROM_EPHEMERAL ] ; then
    33      # Try back to ephemeral store if http failed.
    34      # NOTE: This usage is deprecated and will be removed in the future
    35      if ! grep -q -f $PUB_KEY_FROM_EPHEMERAL $ROOT_AUTHORIZED_KEYS
    36      then
    37              cat $PUB_KEY_FROM_EPHEMERAL >> $ROOT_AUTHORIZED_KEYS
    38              echo "New key added to authrozied keys file from ephemeral store"|logger -t "ec2"
    39
    40      fi
    41      chmod 600 $ROOT_AUTHORIZED_KEYS
    42      chmod 600 $PUB_KEY_FROM_EPHEMERAL
    43
    44  fi
    45
    46  if [ -e /mnt/openssh_id.pub ] ; then
    47          if ! grep -q -f /mnt/openssh_id.pub /root/.ssh/authorized_keys
    48          then
    49                  cat /mnt/openssh_id.pub >> /root/.ssh/authorized_keys
    50                  echo "New key added to authrozied keys file from ephemeral store"|logger -t "ec2"
    51
    52          fi
    53          chmod 600 /root/.ssh/authorized_keys
    54  fi

$ sudo chmod +x /mnt/ec2-fs/usr/local/sbin/get-credentials.sh

$ sudo vi /mnt/ec2-fs/etc/rc.d/rc.local
      1 #!/bin/sh
      2 #
      3 # This script will be executed *after* all the other init scripts.
      4 # You can put your own initialization stuff in here if you don't
      5 # want to do the full Sys V style init stuff.
      6
      7 touch /var/lock/subsys/local
      8 /usr/local/sbin/get-credentials.sh

作業環境をアンマウントします

$ cd
$ sudo umount /mnt/ec2-fs/proc
$ sudo umount -d /mnt/ec2-fs
$ cd /mnt/work/
$ sudo ls
centos4.img  yum-xen.conf

作業環境のゾーンを取得する

$ sudo ec2-metadata -z
placement: ap-southeast-1a

作業環境のEC2インスタンスIDを取得する

$ sudo ec2-metadata -i
instance-id: i-5a8fbe08

EBSの準備、CentOS4イメージの流し込み

ブートイメージ格納用のEBSボリュームを作成する

$ sudo ec2-create-volume --size 10 --availability-zone ap-southeast-1a
VOLUME  vol-64f5ca0c    10              ap-southeast-1a creating        2010-09-16T06:43:39+0000

EBSボリュームをアタッチする

$ sudo ec2-attach-volume vol-64f5ca0c --instance i-5a8fbe08 --device /dev/sdh
ATTACHMENT      vol-64f5ca0c    i-5a8fbe08      /dev/sdh        attaching       2010-09-16T06:50:33+0000

ddコマンドを使ってイメージをEBSボリュームに戻す

$ sudo dd if=/mnt/work/centos4.img of=/dev/sdh
20971520+0 records in
20971520+0 records out
10737418240 bytes (11 GB) copied, 1384.4 seconds, 7.8 MB/s

$ sudo mkdir -p /mnt/ebs
$ sudo mount /dev/sdh /mnt/ebs
$ sudo mv /mnt/ebs/etc/fstab /mnt/ebs/etc/fstab.bak
$ sudo cat /mnt/ebs/etc/fstab.bak | grep -v mnt > /mnt/ebs/etc/fstab
$ sudo cat /mnt/ebs/etc/fstab
/dev/sda1  /         ext3    defaults        1 1
/dev/sda3  swap      swap    defaults        0 0
none       /dev/pts  devpts  gid=5,mode=620  0 0
none       /dev/shm  tmpfs   defaults        0 0
none       /proc     proc    defaults        0 0
none       /sys      sysfs   defaults        0 0

EBSボリュームをデタッチする

$ sudo umount /mnt/ebs
$ sudo ec2-detach-volume vol-64f5ca0c

SnapShotからAMIの作成

SnapShotを作成

# ec2-create-snapshot vol-64f5ca0c --description CentOS4.8-BaseImage
SNAPSHOT        snap-cc74f8a4   vol-64f5ca0c    pending 2010-09-16T07:47:12+0000              91451529646410      CentOS4.8-BaseImage

AMIの登録

# ec2-register --snapshot snap-cc74f8a4 --description="yanase, 20100916" --name="CentOS4.8-i386-20100916" --architecture i386 --root-device-name /d   sda1
IMAGE   ami-02245a50

インスタンスのテスト起動

スタンス起動

# ec2-run-instances ami-02245a50 --block-device-mapping /dev/sdc=ephemeral0 --availability-zone ap-southeast-1a --group cg-yanase --instance-initia   -    shutdown-behavior stop --key cg-yanase -t m1.small
    RESERVATION     r-3c9bad6e      914515296464    cg-yanase
    INSTANCE        i-846050d6      ami-02245a50                    pending cg-yanase       0               m1.small        2010-09-16T08:01:07+000             ap-southeast-1a                     monitoring-disabled                                     ebs                                     pai r t ual

おまけ:起動後の初期設定

ローカルストレージのマウント

# df -h
Filesystem            Size  Used Avail Use% Mounted on
/dev/sda1             9.9G  1.1G  8.3G  12% /
none                  851M     0  851M   0% /dev/shm

# fdisk -l

Disk /dev/sdc: 160.1 GB, 160104972288 bytes
255 heads, 63 sectors/track, 19464 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

Disk /dev/sdc doesn't contain a valid partition table

# mount /dev/sdc /mnt
# df -h
Filesystem            Size  Used Avail Use% Mounted on
/dev/sda1             9.9G  1.1G  8.3G  12% /
none                  851M     0  851M   0% /dev/shm
/dev/sdc              147G  188M  140G   1% /mnt

/dev/sdcのマウントをfstabに記述

# vi /etc/fstab
      1 /dev/sda1  /         ext3    defaults        1 1
      2 /dev/sda3  swap      swap    defaults        0 0
      3 /dev/sdc   /mnt      ext3    defaults        0 0
      4 none       /dev/pts  devpts  gid=5,mode=620  0 0
      5 none       /dev/shm  tmpfs   defaults        0 0
      6 none       /proc     proc    defaults        0 0
      7 none       /sys      sysfs   defaults        0 0

アップデート

# yum update -y
# reboot

時刻を日本に変更

# rm /etc/localtime
# ln -s /usr/share/zoneinfo/Japan /etc/localtime

cronによる時刻同期

# crontab -e
      1 MAILTO=""
      2
      3 ## adjust clock
      4 */5 * * * * /usr/sbin/ntpdate  ntp.jst.mfeed.ad.jp

言語サポート

#  vi /etc/sysconfig/i18n

      1 LANG="C"
      2 SUPPORTED="ja_JP.UTF-8:ja_JP:ja"
      3 SYSFONT="latarcyrheb-sun16"

hostsファイルの変更

# vi /etc/hosts
# chmod 644 /etc/hosts

ちなみにec2-ami-toolsはtarの1.5を必要とするのでCentOS4系標準の状態ではインストールできません。

# wget http://s3.amazonaws.com/ec2-downloads/ec2-ami-tools.noarch.rpm
# rpm -ivh ec2-ami-tools.noarch.rpm
error: Failed dependencies:
        tar >= 1.15 is needed by ec2-ami-tools-1.3-57676.noarch
# tar --version
tar (GNU tar) 1.14

※今回の手順は弊社が行ったテストした方法となり、AmazonEC2上での動作を保証するものではありません

 

JasmineによるJavaScriptのテスト その3

2010年12月7日 by ogura

こんにちは、プログラマのおぐらです。

前回のJasmineによるJavaScriptのテスト その2から1週間ほど空いてしまいましたが、今回もJasmineのチュートリアルをお送りします。

テストの事前準備と後始末

他のテスティングフレームワークと同様に、Jasmineにもテスト毎に事前準備と後始末を行う仕組みがあります。Jasmineでは、テスト準備のためにbeforeEachメソッド、後始末のためにafterEachメソッドが提供されています。

Jasmineのテストは、describeメソッドに渡す無名関数内にbeforeEachitメソッド呼び出しを記述し、さらにそれらに渡す無名関数の中で実際のテストコードを記述するというスタイルになっています。が、厳密な記述をしていくとだいぶ文章が読みづらくなってしまうため、以降の説明ではRSpecなどのフレームワークと同じように「describeメソッドの中」、「itメソッド内」と表記します。厳密さが気になる方は適宜読み替えてください。よろしくお願いします。

beforEachメソッド

describe内に書かれた各itメソッド呼び出しの前に毎回実行されるメソッドがbeforEachメソッドです。前回、「最初のテスト」として以下のテストを作成しました。

これにbeforeEachメソッドを追加したコードを以下に示します。

最初のテストで使用していたarr変数をbeforeEachメソッド内で初期化するようになりました。こうする事によって、arr変数に対して複数のテスト(=itメソッド)を追加していった場合でも、共通する初期化処理を1箇所にまとめることができます。

では、具体例を見てみましょう。

ここでは、Arrayクラスのshiftメソッドに対するテストを追加しました。追加したテストの内容は、

  • shiftメソッドの返り値が配列の先頭要素の1である
  • shiftメソッド実行後のArrayオブジェクトの先頭要素が2に変わっている

というものです。

では実行してみましょう。

テスト実行結果

追加したテストも意図どおりに動作しているようです。

今回の例では、それぞれのitメソッド間で共有しているarr変数の内容を、新たに追加した「shiftメソッドにより配列の先頭要素を取り出す事ができる」で変更していますが、既存の「lengthプロパティで配列長を取得する事ができる」の結果には影響していません。これは、itメソッドが実行される前にbeforeEachメソッドが毎回実行され、beforeEachメソッド内のarr = [1, 2, 3];という箇所で、毎回arr変数が初期化されているためです。

afterEachメソッド

beforeEachメソッドとは逆に、describe内の各itメソッドが実行された直後に毎回実行されるのがafterEachメソッドです。たいていの場合はbeforeEachメソッドによる初期化処理のみで事足りますが、明確に後始末を行う必要がある場合にはこのメソッドを利用します。

例えばDOM操作を伴うテストの場合、beforeEachメソッドで「document.bodyにテスト用のDOM要素を追加する」という処理を行い、afterEachメソッドでは「追加したDOM要素を削除する」という処理を書いておけば、テスト同士が影響しないようにすることができます。

次回に続きます。

 

PAGE TOP