プログラミング」カテゴリーアーカイブ

KarmaでDOM操作のテストをする

このページでは、KarmaでDOM操作のテストをする方法を紹介します。

テスト対象

例として、下記のコードをテストしたいとします。index.htmlを開くと、メッセージが表示されるものです。

<!-- index.html -->
<!DOCTYPE html>
<head>
  <meta charset="utf-8">
</head>
<body>
  <p id="message"></p>
  <script src="js/jquery.js">
  <script src="js/app.js">
  <script>
    $(function() {
      app.showMessage('init');
    }());
  </script>
</body>
</html>
// app.js
(function(global) {
  'use strict';
  global.app = {
    showMessage: function(message) {
      $('#message').text(message);
    }
  };
}(this.self));

フォルダ構成は下記の通りです。appフォルダにテスト対象のコードを入れ、specフォルダにテストコードを書いていきます。

Project
  ├─ app
  │    ├─ index.html
  │    └─ js
  │       ├─ jquery.js
  │       └─ app.js
  └─ spec

テスト環境構築

karma-html2js-preprocessorのインストール

karma-html2js-preprocessorはKarmaのプラグインであり、HTMLファイルをjsから読み込むことができるようになります。

$ npm install karma-html2js-preprocessor --save-dev

karmaの設定ファイルを変更し、filesとpreprocessorsにHTMLファイルを追加します。

// karma.conf.js
module.exports = function(config) {
  config.set({
    ...
    files: [
      'app/js/jquery.js',
      'app/js/app.js',
      'app/*.html'
      'spec/**/*.js',
    ],
    preprocessors: {
      'app/*.html': ['html2js']
    },
    ...
  });
};

テストコードの記述

specフォルダにテストコードappSpec.jsを作成し、beforeEachでdocumentのbodyにindex.htmlを読み込みます。テスト対象のコードでjQueryを使用しているので、テストコードからも使用しています。

// appSpec.js
describe('app', function() {
  var text = 'text';
  beforeEach(function() {
    document.body.innerHTML = __html__['app/index.html'];
  });
  it('should show message', function() {
    app.showMessage(text);
    expect($('#message').text()).toEqual(text);
  });
});

画像読み込みエラー

index.htmlで画像ファイルを読み込んでいる場合、テスト実行時にWARNINGが表示されます。

<!-- index.html -->
<!DOCTYPE html>
<html>
  ...
  <img src="img/image.png">
  ...
</html>
$ npm test
...
WARN [web-server]: 404: /img/image.png
...

このエラーが表示されないようにするには、プロキシを介して画像ファイルを読めるようにします。画像ファイルはファイルの変更監視が必要ないのと、ブラウザ起動時にscriptタグで読み込まないので、watchとincludedをfalseにします。プロキシには、ベースとなるフォルダのパスを基点にするために’base’を付加したパスを設定します。

// karma.conf.js
module.exports = function(config) {
  ...
  files: [
    'app/js/jquery.js',
    'app/js/app.js',
    'app/*.html',
    {pattern: 'app/**/*.png', watched: false, included: false, served: true},
    'spec/**/*.js'
  ],
  proxies: {
    '/img/': '/base/img/'
  },
  ...
};

Karmaでカバレッジを取る

このページでは、Karmaのテスト実行時にカバレッジを取る方法を紹介します。

カバレッジ取得環境の構築

karma-coverageのインストール

karma-coverageはkarmaのプラグインであり、テストを実行するとカバレッジが取れます。karmaのテスト環境構築方法はこちらで紹介しています。

$ npm install karma-coverage --save-dev

karmaの設定ファイルを修正します。reportersに’coverage’を追加し、preprocessorsにカバレッジを取りたいファイルを指定します。

// karma.conf.js
module.exports = function(config) {
  config.set({
    ...
    files: [
      'app/js/jquery.js',
      'app/js/app.js',
      'test/**/*.js'
    ],
    reporters: ['progress', 'coverage'],
    preprocessors: {
      'app/js/app.js': ['coverage']
    }
    ...
  });
};

カバレッジ取得

Karmaのテストを実行すると、自動的にカバレッジが取得します。取得したカバレッジはcoverageフォルダにHTML形式で保存されます。

$ npm test
(karma startのことです)

coverage/index.html
karma-coverage
 

ちなみに

カバレッジデータはテストを実行すれば作成されるため、コミットする必要はありません。バージョン管理している場合はignoreに追加しておきましょう。

[.gitignore]
...
coverage

karma.conf.jsをwatchする

このページでは、Karmaの設定ファイルであるkarma.conf.jsの変更を検知して、テストを再実行する方法を紹介します。

はじめに

karma.conf.jsには、テスト対象やテストコードのファイルパスや記述します。autoWatchをtrueにすればファイルの変更を検知できますが、karma.conf.js自体の変更を検知できません。テスト実行中にファイルを増やすこともあるため、karma.conf.jsもwatchしたいと思います。
karmaのテスト環境構築方法はこちらで紹介しています。

nodemonのインストール

nodemonを使うと、ファイルの変更を検知してタスクを再実行できます。

$ npm install nodemon --save-dev

ファイルの監視とテストの実行

監視するファイルと実行するタスクを指定して、nodemonを実行します。karma.conf.jsを変更すると、テストが止まって再実行されます。

$ node_modules/.bin/nodemon --watch karma.conf.js --exec node_modules/.bin/karma test

コマンドの整理

毎回長いコマンドを書くのも大変なので、npm testでファイルの監視を含めたテストを実行できるようにします。

[package.json]
{
  ...
  "scripts": {
    "test": "nodemon --watch karma.conf.js --exec karma start"
  },
  ...
}

下記コマンドでテストが実行できます。

$ npm test

Karma+JasmineでJavaScriptをテストする

このページでは、KarmaというテストランナーとJasmineというテストフレームワークを使って、JavaScriptをテストする方法を紹介します。

関連記事

このページの続きとして、以下の関連記事があります。順番は関係ないので、必要なものを見てください。

テスト対象

例として、下記のコードをテストしたいとします。index.htmlを開くと数値の合計値が表示されるものです。

<!-- index.html -->
<!DOCTYPE html>
<head>
  <meta charset="utf-8">
</head>
<body>
  <p id="message"></p>
  <script src="js/app.js">
  <script src="js/jquery.js">
  <script>
    $(function() {
      var num = app.sum(1, 2);
      app.showMessage(num);
    }());
   </script>
</body>
</html>
// app.js
(function(global) {
  'use strict';
  global.app = {
    sum: function(a, b) {
      return a + b;
    },
    showMessage: function(message) {
      $('#message').text(message);
    }
  };
}(this.self));

フォルダ構成は下記の通りです。appフォルダにテスト対象のコードを入れ、specフォルダにテストコードを書いていきます。

Project
  ├─ app
  │    ├─ index.html
  │    └─ js
  │       ├─ jquery.js
  │       └─ app.js
  └─ spec

テスト環境構築

事前準備

Node.jsはインストール済みとして、package.jsonがない場合は作ります。設定は自由に。

$ npm init

karmaのインストール

npmからkarmaをインストールします。

$ npm install karma --save-dev

karmaの設定ファイルを作成します。テストフレームワークとしてJasmine、ブラウザにChrome、テストコードのディレクトリをspecフォルダ以下のjsファイルに指定します。

$ node_modules/.bin/karma init
Which testing framework do you want to use ?
Press tab to list possible options. Enter to move to the next question.
> jasmine
Do you want to use Require.js ?
This will add Require.js plugin.
Press tab to list possible options. Enter to move to the next question.
> no
Do you want to capture any browsers automatically ?
Press tab to list possible options. Enter empty string to move to the next question.
> Chrome
>
What is the location of your source and test files ?
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".
Enter empty string to move to the next question.
> app/js/jquery.js
> app/js/app.js
> spec/**/*.js
WARN [init]: There is no file matching this pattern.
>
Should any of the files included by the previous patterns be excluded ?
You can use glob patterns, eg. "**/*.swp".
Enter empty string to move to the next question.
>
Do you want Karma to watch all the files and run the tests on change ?
Press tab to list possible options.
> yes
Config file generated at ".../Project/karma.conf.js".

テストコードを書く

app.jsのテストコードとして、specフォルダにappSpec.jsを作成します。appオブジェクトのsum関数をテストします。

// appSpec.js
describe('app', function() {
  it('should sum numbers', function() {
    expect(app.sum(1, 2)).toEqual(3);
  });
});

テストを開始します。するとブラウザが立ち上がりテストコードが実行されます。「SUCCESS」と表示されていて、テストが成功したことがわかります。テストにかかった時間も表示されるため、処理速度の目安にすることもできます。

$ node_modules/.bin/karma start
INFO [karma]: Karma v0.12.37 server started at https://localhost:9876/
INFO [launcher]: Starting browser Chrome
INFO [Chrome 43.0.2357 (Mac OS X 10.10.4)]: Connected on socket IFN6So2x3Bv54q3o_ch4 with id 97782541
Chrome 43.0.2357 (Mac OS X 10.10.4): Executed 1 of 1 SUCCESS (0 secs / 0.002 secChrome 43.0.2357 (Mac OS X 10.10.4): Executed 1 of 1 SUCCESS (0.005 secs / 0.002 secs)

karma_chrome

コマンドの整理

上記では「node_modules/.bin/karma」を実行しており、binのパスまで指定する必要がありますが、npm経由でコマンドを実行すると、パスを指定しなくても実行できます。

[package.json]
{
  ...
  "scripts": {
    "test": "karma start"
  },
  ...
}

下記コマンドでテストが実行できます。

$ npm test

ちなみに

DOM操作を行っているappのshowMessage関数のテスト方法はこちらを紹介しています。

PhoneGapアプリの公開用APKを作る

このページでは、PhoneGapで作ったAndroidアプリの公開用APKを作るまでを紹介します。APKは「android application package file」の略です。

事前準備

証明書を作る

Google playにアプリを公開するには、アプリに署名する必要があります。これは、アプリの正当性を保証するためです。
まず、署名するための証明書を作ります。keytoolはJDKに入っているので、パスが通っていればどこでも実行できます。パスワードなど色々聞かれるので、好きに答えてください。「よろしいですか?」と聞かれたら、「y」と答えれば次に進みます。

  今回の設定値
エイリアス名 shoarai
アプリ名 app
有効日数  10000
$ keytool -genkey -v -keystore .keystore -alias shoarai -keyalg RSA -validity 10000
> キーストアのパスワードを入力してください:
> 新規パスワードを再入力してください:
> 姓名を入力してください。
> 組織単位名を入力してください。
> 組織名を入力してください。
> 都市名または地域名を入力してください。
> 都道府県名を入力してください。
> この単位に該当する2文字の国コードを入力してください。
> ...でよろしいですか。
10,000日間有効な2,048ビットのRSAの鍵ペアと自己署名型証明書(SHA256withRSA)を生成しています
	ディレクトリ名: ... の鍵パスワードを入力してください
	(キーストアのパスワードと同じ場合はRETURNを押してください):

公開用APKを作る

APKを生成する

アプリの実装が完了したらリリースビルドし、APKを生成します。platforms/android/ant-buildにapp-release-unsigned.apkというファイルが生成されます。appの部分にはアプリ名が入ります。

$ phonegap build --release

署名する

事前に作った証明書でapkに署名します。

$ jarsigner -verbose -keystore .keystore app-release-unsigned.apk shoarai

最適化する

Google playにアプリを公開するには、最適化もする必要があります。最適化後のapkのファイル名をapp.apkとします。これで公開用APIの完成です。

$ zipalign -v 4 app-release-unsigned.apk app.apk

エラー

前述の最適化にて、zipalignコマンドが見つからないことがあります。

-bash: zipalign: command not found

zipalignはANDROID SDKのBuild-toolsに入っています。もしまだBuild-toolsが入ってないなら、Android SDK Managerからインストールしましょう。また、パスを通さないと実行できません。パスを通すのが面倒であれば、下記のように直接アクセスして実行してもいいです。

$ /Applications/android-sdk-macosx/build-tools/21.1.1/zipalign ...

ちなみに

APK生成、署名、最適化をまとめて行えるように下記のようなバッチファイルを作り、証明書と一緒にプロジェクトフォルダに置いておくと楽ですね。

MY_DIRNAME=$(dirname $0)
cd $MY_DIRNAME
# 設定値
UNSIGNED_FILE=platforms/android/ant-build/app-release-unsigned.apk
SIGNED_FILE=app.apk
ALIAS=shoarai
phonegap build --release
jarsigner -verbose -keystore .keystore $UNSIGNED_FILE $ALIAS
zipalign -v 4 $UNSIGNED_FILE $SIGNED_FILE

警告が表示される(2015-12-24追記)

いつの間にか、署名時に下記の警告が表示されるようになっていました。

警告:
-tsaまたは-tsacertが指定されていないため、このjarにはタイムスタンプが付加されていません。
タイムスタンプがないと、署名者証明書の有効期限(2043-05-10)後または将来の失効日後に
、ユーザーはこのjarを検証できない可能性があります。

JDK1.7からタイムスタンプが必要になったそうです。署名を下記のように修正すると警告が表示されなくなります。

$ jarsigner -verbose -tsa https://timestamp.digicert.com -keystore .keystore app-release-unsigned.apk shoarai

Nightwatch.jsのテストをいろいろなブラウザで動かす

WebアプリのUIテストができるSeleniumですが、それをラップして色々なAPIを提供してくれるのがNightwatch.jsです。アプリをJavaScriptで書いていて、テストも同じ言語で書きたい人には特におすすめです。

ただNighwatch単体だと不十分なところがあり、さらなるラッパーを探してたところhttps://github.com/sethmcl/nightwatchを発見。使ってみたところ動かず;そこで少し修正したものを公開します。特徴は下記のとおり。

  • Selenium Serverを事前に動かさなくていい。
  • ChromeとかIEでテストできる。
  • APIが簡単に作れる。

使い方

まずgithubからクローンしてください。

$ git clone https://github.com/shoarai/nightwatch.git
$ cd nightwatch
$ npm install

下記のコマンドで、デフォルトのテストが動きます。
Selenium Serverも組み込まれていて、勝手に動いて勝手に止まります。

$ npm test

ブラウザ選び

デフォルトのテストはFirefoxで動きますが、Google Chrome、Internet Explorerも選べます。Nigthwatchfile.jsを修正してください。

test_settings: {
  default: {
    ...
      desiredCapabilities: {
      browserName: 'firefox',
      // browserName: 'chrome',
      // browserName: 'internet explorer',
      ...

WindowsでChromeを使いたい場合は下記も修正してください。

selenium: {
  cli_args: {
    // 'webdriver.chrome.driver': './node_modules/.bin/chromedriver',      // in Mac
    'webdriver.chrome.driver': './node_modules/.bin/chromedriver.cmd',  // in Windows
    ...

APIを作る

tests/spec/search.jsでは、pageオブジェクトにあるhomepagesearch_resultsの2つのモジュールを使っています。これらを使うためにはbeforeにある処理が必要です。

var path = require('path');
module.exports = {
  tags: ['sanity', 'search'],
  before: function(client) {
    require('nightwatch-pages')(client, path.resolve(__dirname, '..', 'pages'));
  },
  'Bing search from homepage': function (client) {
    var searchTerm = 'selenium';
    client
      .page.homepage.load()
      .page.homepage.search(searchTerm)
      .page.search_results.assertResults(searchTerm)
      .page.search_results.navImages()
      .saveScreenshot('./screen/selenium.png')
      .end();
  }
};

tests/pages/search_results.jsに上記のsearch_resultsモジュールの関数が書いてあります。新しいモジュールを追加したい場合は、tests/pagesにモジュール名にしたい名前のファイルを追加してください。

module.exports = {
  selectors: {
    'resultDiv': '#b_results',
    'navImages': 'nav ul li:nth-child(2) a'
  },
  assertResults: function (text) {
    return this.client.assert.containsText(this.selectors.resultDiv, text);
  },
  navImages: function () {
    return this.client.click(this.selectors.navImages);
  }
};

以上です。よりよいテスト自動化を。

PhoneGapでアプリを作る

このページでは、PhoneGapをインストール後にサンプルプロジェクトを作成し、エミュレータで起動するまでを紹介します。

PhoneGapの導入

PhoneGapをインストールし、phonegapコマンドを使えるようにします。

事前準備

  • Node.jsがインストール済み
  • Antがインストール済み
  • Android SDKがインストール済み

PhoneGapのインストール

npmからPhoneGapをインストールします。

$ npm i -g phonegap

インストールが終わったら、PhoneGapのインストール状況を確認します。PhoneGapのコマンド一覧などが参照できます。

$ phonegap
...
Commands:
  create <path>        create a phonegap project
  build <platform>     build a specific platform
  install <platform>   install a specific platform
  run <platform>       build and install a specific platform
  local [command]      development on local system
  remote [command]     development in cloud with phonegap/build
  platform [command]   update a platform version
  plugin [command]     add, remove, and list plugins
  help [command]       output usage information
  version              output version number
...

プロジェクトの作成

PhoneGapコマンドから以下の内容を持つプロジェクトを作ります。このプロジェクトはAndroidやiOSなどのプラットフォームに共通のプロジェクトです。

プロジェクト名

まず、フォルダ名、パッケージ名、プロジェクト名は以下の通りとします。 ちなみにフォルダ名以外は未入力でも、デフォルトで設定されるので特に決めなくても大丈夫です。

  今回の入力値 デフォルト値
フォルダ名 Hello -(未入力不可)
パッケージ名 com.shoarai.hello com.phonegap.helloworld
プロジェクト名 HelloWorld Hello World

プロジェクトの生成

phonegapコマンドで上記の内容を持ったプロジェクトを生成します。

$ phonegap create Hello com.shoarai.hello HelloWorld

作成されるHelloフォルダの構成は以下の通りです。wwwフォルダ内にプラットフォームに共通するHTML、JavaScriptファイルがあります。

Hello
  ├─ .cordova
  ├─ merges
  ├─ platforms
  ├─ plugins
  └─ www

Helloフォルダに移動します。

$ cd Hello
.../Hello>

アプリケーションの作成

作成したプロジェクトからアプリケーションの実行ファイルを作成します。今回はAndroidアプリを作成しますが、PhoneGapはiosやWindowsPhoneのアプリも作成できます。

プロジェクトのビルド

phonegapコマンドでプロジェクトをビルドします。AndroidアプリケーションとしてビルドするためにAndroid SDKを利用しています。

$ phonegap build android
...
[phonegap] successfully compiled Android app

Androidアプリとしてビルドすると、platformsフォルダ内にandroidフォルダが作成されます。また、iOSとしてビルドするとiosフォルダが追加されることになります。 それぞれのフォルダ内に生成されるplartforms/…/wwwフォルダは、Hello/wwwフォルダがコピーされたものです。つまり、Hello/www内を編集して、 ビルドすると指定したplartforms/…/wwwが更新されることになります。

Hello
  ├─ .cordova
  ├─ merges
  ├─ platforms
  │      └─ android
  │             ├─ assets
  │             │     └─ www
  │              ...
  ├─ plugins
  └─ www

エラー

ビルド時に起こったエラーを記述しておきます。

Please install Android target 19 (the Android newest SDK)

これはPhoneGapが対応するAndroid SDKが入ってないよ!ということです。解決策は2つ。「①対応するSDK Platformをインストールする」または「②PhoneGap側のSDKの対応バージョンを変更する」です。 SDK側を合わせるか、PhoneGap側を合わせるかということです。
①は、エラー文にあるSDKバージョン(ここでは19)をインストールします。
②は、対応するバージョンを.cordova/lib/android/cordova/3.1.0(PhoneGapのバージョン)/frameworkフォルダ内のproject.propertiesファイルのtargetの値で確認できます。 ここではtarget=android-19をインストール済みのSDKのバージョン(android-18など)に変更します。

アプリケーションの実行

作成したAndroidアプリを実行します。

エミュレータの起動

まず、アプリを実行するエミュレータを起動します。AVD(Android Virtual Device Manager)を実行し、起動するエミュレータのAVD名を確認します。もしくは起動するAVDを新規作成します。

$ android avd

ここでは、AVD名がNexus5のエミュレータを起動します。

$ emulator -avd Nexus5

アプリケーションの実行

作成したAndroidアプリケーションをエミュレータで実行します。

$ phonegap run android
...
[phonegap] successfully installed onto emulator

phonegap
Androidアプリケーションをエミュレータで実行できました!実行前に実機を接続しておくと、優先して実機で実行されます。 このときは[phonegap] successfully installed onto deviceが表示されます。

ちなみに

今回は説明のためにアプリケーションのビルドと実行を分けましたが、phonegap run androidを実行すれば、phonegap build androidを実行しなくてもアプリケーションの実行ファイルが生成されてます。