2014年1月31日金曜日

ニフティクラウド RDBをzabbixで監視する方法

■概要
ニフティクラウドRDBにはモニタリングの機能がありコントールパネル上からCPU使用率やDBのコネクション数を確認することができます
ただ、グラフで閲覧できるだけで閾値を設定してアラートを上げたりすることはできません
また監視データも過去1週間しか確認できないため過去のデータに遡ることが限定されてしまいます
ニフティクラウドRDBでは3306ポートと通信できるだけなのでこのポートを使用してzabbixで監視をしてみたいと思います

■環境
CentOS 5.10 (64bit)
Zabbix 2.2.1
MySQL 5.5.32 (ニフティクラウドRDB)

■設定手順
以下の手順はすべてzabbixサーバ側で実施します
当たり前ですが、ニフティクラウドRDB側のサーバにsshログインして何かするといことはありません(できません)

1. 監視スクリプトの作成
emacs /etc/zabbix/externalscripts/mysql_watch_scrpt.sh
Zabbix 2.2.1 では外部スクリプトは上記のパスにファイルを作成すると自動で読み込んでくれるようです
※すいません、スクリプトは参考サイトさんのものをそのまま利用させていただきました
#!/bin/sh

USER=$1
PASS=$2
HOST=$3
KEY=$4

mysqladmin -u $USER -p$PASS -h $HOST  extended-status | awk '{if($2 == "'$KEY'") print $4}'
chown zabbix:zabbix /etc/zabbix/externalscripts/mysql_watch_scrpt.sh
chmod 755 /etc/zabbix/externalscripts/mysql_watch_scrpt.sh
zabbixユーザが実行できるように権限を付与します

sh /etc/zabbix/externalscripts/mysql_watch_scrpt.sh [username] [password] [ニフティクラウドRDBで提供されているIPアドレス] [mysqladminで取得できる項目名]
実行して問題なく値が取得できることを確認します
存在しない項目名を入力すると空文字が返ってきます

2. zabbix_server.conf設定(確認)
emacs /etc/zabbix/zabbix_server.conf
ExternalScripts=/etc/zabbix/externalscripts
上記の設定が記載されていることを確認します

3. ニフティクラウドRDBサーバのホスト追加
設定 -> ホスト -> ホストの作成からニフティクラウドRDBのサーバを追加します
IPアドレスにニフティクラウドRDBで提供されたIPアドレスを入力します(プライベート側のIPで大丈夫です)
ホストグループやホスト名は適宜変更してください

マクロのタグから以下のとおりマクロを設定します
  • {$RDB.IP}・・・ニフティクラウドRDBで提供されたIPアドレス
  • {$RDB.USERNAME}・・・mysqlに接続するためのユーザ名
  • {$RDB.PASSWORD}・・・上記ユーザのパスワード
※マクロ名も適宜変更して問題ございません

4. アイテム追加
設定 -> ホスト -> [追加したホスト名のアイテム] -> アイテムの作成でホストにアイテムを追加します
ホストにテンプレートを紐付けてテンプレートに対してアイテムを追加しても問題ございません
以下のように設定します

ポイントは
  • タイプ:外部チェック
  • キー:mysql_watch_scrpt.sh[{$RDB.USERNAME},{$RDB.PASSWORD},{$RDB.IP},"Open_files"]
を設定できていれば問題ないです
仕組みとしては作成したスクリプトの引数にマクロで設定した値を渡すことでスクリプトを実行し取得します
更新間隔やヒストリ、トレンドの値はデフォルの値を使用しておりますので適宜変更してください
また、キーの最後の"Open_files"の部分を変更することが他の項目を監視することができます(今回は例としてOpen_filesを入れております)

5. トリガー追加
設定 -> ホスト -> [追加したホスト名のトリガー] -> トリガーの作成でホストにトリガーを追加します
こちらもアイテム同様テンプレートが紐付いているならばテンプレートに追加しても問題ございません
今回は以下のように設定しました

必ずアラートが上がるように閾値は1を設定しています

6. 動作確認
設定が完了したらデータが取得できているか確認します
監視データ -> 概要 -> タイプ:データ を選択し作成したアイテムの項目が取得できている確認します
値が取得できていない場合は権限が付与できているかやアイテムの指定にスペルミスがないか等確認してください
それでもダメな場合はzabbix_server.confでDebugLevel=2に設定しエラーの内容を出力してどんなエラーになっているか確認してトラブルシュートしてください

mysqladminコマンドを使用して監視できる項目を監視することができました
が、これではサーバ自体のリソースCPUやメモリ使用量を監視することができないのでなんとかその辺も監視することができないか模索してみたいと思います
(というかそもそもそういうのを運用、監視しなくていいためにiPaaSという分野が生まれたような。。。相反することを実はやっている。。。)

■参考サイト

2014年1月29日水曜日

Zabbixでロックファイルを監視する方法

■概要
よくあるケースとしてロックファイルが正常に削除されているか、ずっとロック状態になっていないか
という確認をしたいことがあるかと思います
今回はzabbixでロックファイルの存在を監視する例を紹介したいと思います

■環境
CentOS 5.9 64bit
zabbix 2.2.1

■アイテムを追加
以下のアイテムを作成します
  • 名前:lockfile exists check
  • タイプ:Zabbixエージェント
  • キー:vfs.file.exists[/tmp/test.lock]
  • 更新間隔(秒):30
名前、キーで指定するlockfile名は適宜変更してください
その他の項目については特に変更する必要はございません
更新間隔は30秒を指定していますがここの設定によってlockファイルが何秒存在していたらアラートをあげるという部分の条件になるので必要に応じて変更してください
アイテム追加は新規作成したテンプレートか既存のテンプレートに追加した方がいいと思いますが、ホストに直接アイテムを追加しても問題ないです

■トリガー設定
以下のトリガーを作成します
  • 名前:lockfile exists check
  • 条件式:{Original Template:vfs.file.exists[/tmp/test.lock].last()}+{Original Template:vfs.file.exists[/tmp/test.lock].prev()}>1
  • 深刻度:警告
条件式は「Original Template」というテンプレート内にアイテムを作成した場合の設定方法となります

■動作確認
監視データ -> 概要から追加したアイテムの値が取得できているか確認します
ファイルが存在している場合は1が、存在していない場合は0が返ってきます
今回の設定では30秒間隔でデータを取得しにいくようにしているのですぐにアラートがあがると思います
アクションは必要に応じて追加してください

●仕組み
ロックファイルが存在しているどうかを調べることで実現しています
トリガーの設定で前回の値と今回の値を加算した結果が1より大きかった場合にアラートをあげるようにしています
これは例えば30秒で値を取得しているときに30秒前にファイルが存在して今回取得したときにもファイルが存在している場合にアラートあがる動きになっています
つまり、指定した更新間隔時間以上、ロックファイルが存在している場合にアラートをあげてくれています
ただ、この場合はだとファイル作成 -> 削除 -> 作成 -> 削除と繰り返した場合に、たまたま値を取得したタイミングが前回も今回も作成した場合にぶち当たるとちゃんとロックファイルは削除されているのにアラートがあがってしまうという過剰検知を引き起こす可能性もあります

たぶんもっと良い仕組みはあるんだろうけどパッと思いついたのがこの方法でした

2014年1月28日火曜日

Zabbixを1.8から2.2にアップグレードしたときのメモ

■背景
Zabbixがあまりにも古かったのでアップグレードしました
バージョンは 1.8 から 2.2 にアップグレードしました
残念ながら今回既存のデータは引き継げませんでした
やり方によっては引き継げるようで、1.8 -> 2.0 -> 2.2 という順番でアップグレードすればできるみたいです
既存のデータが引き継げていないので正直新しくインストールしているやり方と全く同じですが、既存のZabbix環境をアップグレードするための方法となります
また、今回新しいバージョンのインストールはyumからでなくソースコードから実施しました

■環境
CentOS 5.10 (64bit)
Zabbix 1.8.15 -> 2.2.1
php 5.3.3
MySQL 5.5.32 (ニフティクラウドRDB)
※古いZabbix、phpは共にyumでインストール

■アップグレード方法
0. 事前準備
service zabbix-server stop
service zabbix-agent stop
  事前にプロセスは停止しておきましょう

1. zabbix-server, zabbix-agent のアップグレード
cd /var/tmp
wget http://downloads.sourceforge.net/project/zabbix/ZABBIX%20Latest%20Stable/2.2.1/zabbix-2.2.1.tar.gz?r=http%3A%2F%2Fwww.zabbix.com%2Fjp%2Fdownload.php&ts=1390818737&use_mirror=jaist
  ダウンロードできない場合は http://www.zabbix.com/jp/download.php から最新の安定版のパッケージをダウンロードしてください
tar xvzf zabbix-2.2.1.tar.gz
cd zabbix-2.2.1
./configure --enable-server --enable-agent --with-mysql --enable-ipv6 --with-net-snmp --with-libcurl --with-libxml2
  「--with-hogehoge」という感じで指定すると様々な追加パッケージを同時にインストールすることができるようです
  詳細は「./configure --help」で確認できます、今回は公式ドキュメントにあるMySQLを使ってzabbixを使用するためのオプションで指定しました
  「Now run 'make install'」と出れば configure は完了です
make
make install
zabbix_agentd -V
zabbix_server -V
  「Zabbix server v2.2.1 (revision 40808) (09 December 2013)」と表示されればインストール完了です
  上記バイナリファイルは/usr/local/sbin/配下にインストールされています

2. zabbixのバージョンを切り替える
cd /usr/sbin
  yumでインストールした場合はバイナリファイルは/usr/sbinにあります
mv zabbix_agentd zabbix_agentd_1.8.15
mv zabbix_server_mysql zabbix_server_mysql_1.8.15
mv zabbix_agent zabbix_agent_1.8.15
ln -nfs /usr/local/sbin/zabbix_server zabbix_server
ln -s /usr/local/sbin/zabbix_agent zabbix_agent
ln -s /usr/local/sbin/zabbix_agentd zabbix_agentd

3. 新規DBへの移行
mysqldump -u username -p zabbix -h hostname > zabbix_backup.dmp
  念のため1.8時代の情報はdumpしておきます
mysql -u username -p zabbix -h hostname
mysql> drop database zabbix;
mysql> create database zabbix;
cd /var/tmp
cd zabbix-2.2.1/database/mysql
mysql -u username -p zabbix -h hostname < schema.sql
mysql -u username -p zabbix -h hostname < images.sql
mysql -u username -p zabbix -h hostname < data.sql
  順番は schema.sql -> images.sql -> data.sql の順番でやる必要があります(外部キー制約の関係上)
  data.sqlでデフォルトテンプレートやデフォルトユーザをINSERTします

4. UI更新
cd /usr/share
  yumでインストールした場合のUIは/usr/share/zabbixにあります
mv zabbix zabbix_1.8.15
  既存のUIのソースは念のためバックアップしておきます
cd /var/tmp
cd zabbix-2.2.1/frontends/
cp -r php/ /usr/share/zabbix_2.2.1
cd /usr/share
ln -s zabbix_2.2.1/ zabbix

5. 各種起動
service zabbix-server start
service zabbix-agentd start
service httpd start
tailf /var/log/zabbix/zabbix_server.log
  念のためログを確認してエラーが出ていないことを確認します

6. WebUIでセットアップ
http://hostname/zabbix/setup.php にアクセスしてzabbixのセットアップを実施します

セットアップの最後で zabbix.conf.php を /usr/share/zabbix_2.2.1/conf/zabbix.conf.php に作成しようとします
うまく作成できない場合はフォルダの権限を変更してみてください(権限はzabbix:zabbixでやってください)
それでもダメな場合は「Download configuration file」から手動でダウンロードしサーバに配置してください
配置後に「retry」するとOKになると思います

残念ながら既存環境の移行はできませんでしたが、同一サーバ上での zabbix のアップグレードは行えたのでよしとしましょう

■参考サイト

■Tips
makeするための環境等もともと整っている環境で実施したのでその辺のインストール手順は省略しています

ユーザやグループの追加も既存のzabbix:zabbixを使用しています

2.2.1からDBの自動マイグレート機能がついており、それにより既存のデータを簡単に引き継げるようになったのですが、1.8から2.2で自動マイグレート機能を使用しようとすると起動時に「Cannot upgrade database: the database must correspond to version 2.0 or later. Exiting ...」というエラーが出てマイグレートできません
冒頭にも記載しましたが、2.2.1のDB自動マイグレート機能を使用したい場合は一度 1.8 -> 2.0 のアップグレードを実施したあとで 2.0 -> 2.2 のアップグレードも実施する必要があります

WebUIで「Database error: Error connecting to database [Too many connections]」が表示される場合はMySQLの max_connections の値を30以上にしてみてください
どうやらzabbixのWebUIからは毎回30弱のDBコネクションを張るようなのでそれ以下の設定だと上記エラーが出るようです

2014年1月22日水曜日

log4jで同一カテゴリに複数のAppenderを割り当てる際に注意すること

例えば

log4j.category.com.kakakikikeke

というカテゴリに対してログファイルへの出力とコンソールへの出力をしたい場合に

log4j.category.com.kakakikikeke=DEBUG, FILE
log4j.category.com.kakakikikeke=DEBUG, CONSOLE

と記載すると後に記載しているCONSOLEのAppenderだけが有効になってしまいログファイルへの出力ができなくなってしまいます
なので複数のAppdenderを割り当てる場合は

log4j.category.com.kakakikikeke=DEBUG, FILE, CONSOLE

のように1行に複数のAppenderをカンマ区切りで記載してあげる必要があります

当たり前といえばあたり前なのですがちょっとハマったのでメモしておきます

2014年1月17日金曜日

ニフティクラウドmobile backendでSNS連携(Twitter)をやってみた

今回作成したソースコードは以下で公開しています

https://github.com/kakakikikeke/android-ncmb-sample

以下にポイントを紹介します

●SNS連携の機能について
自身のTwitterAPI用のKeyを使ってTwitterAPIを呼び出すことができるようになります
APIの認証部分(OAuth)をサポートしてくれており認証後はTwitterのAPIをコールすることができます
認証後はログインした利用者のタイムラインやツイート情報を表示することができます

●Twitter DeveloperでTwitterAPIを実行するためのConsumerKeyとConsumerSecretKeyを取得します
https://dev.twitter.com/apps にアクセスしアプリを作成しそれぞれのKeyを取得します
まず、取得したConsumerKeyはコンパネに設定します
アプリ設定 -> SNS連携 -> Twitter連携 ->  Twitter Consumer Key
の部分です
またソースコード内でもConsumerKeyとConsumerSecretKeyの2つを使います

●Twitter Developerで作成したアプリの権限設定を見直します
以下の箇所が設定されているか確認します
設定されていないと
401:Authentication credentials (https://dev.twitter.com/pages/auth) were missing or incorrect. Ensure that you have set valid consumer key/secret, access token/secret, and the system clock is in sync.
というエラーとなりうまく利用できません
  • Access level・・・「Read, write, and direct messages」になっているか確認します
  • Callback URL・・・必ず何かを設定してください
  • Sign in with Twitter・・・「Yes」になっているか確認します

●twitter4jの配置
twitter4jのライブラリをアプリのビルドパスに配置する必要があります
配置するjarファイルは「twitter4j-core-X.X.X.jar」だけで大丈夫です
バージョンは3.0.5を利用して下さい、古いライブラリだとAPIコールがhttpとなっており403エラーが発生してしまいます

■参考サイト

2014年1月16日木曜日

【Twitter】twitterIdとscreenNameを調べる方法

APIの仕様変更とかでいろいろと死んでいるサイトがいるので生存しているサイトを忘れないようにメモ

http://nojimi.dip.jp/idsnconv/

http://twitter.com/intent/user?screen_name=kakakikikeke
のソースコード内にtwitterIdがあるのでそれでもわかるみたい

  • twitterId・・・普遍的なもの、アカウント作成時から変更することはできない、基本はユーザの目につくものではない、数字の羅列で自動生成される
  • screenName・・・Twitterのアカウント名みたいなのもの、変更可能でユーザが目にするアカウント情報

2014年1月15日水曜日

minaをインストール

■環境
CentOS 5.9
Ruby ruby 1.9.3p125
gem 2.1.4
mina 0.3.0

■インストール
cd /var/tmp
touch Gemfile
vim Gemfile
cat Gemfile
source :rubygems

gem 'mina'
bundle install --path vendor/bundle
bundle exec mina init
  すると「/var/tmp/config/deploy.rb」が作成されます

■設定
vim /var/tmp/config/deploy.rb
set :domain, 'hostname'
set :deploy_to, '/var/tmp/deploy'
set :user, 'root'
set :repository, 'https://github.com/kakakikikeke/ruby-kakabot.git'

#invoke :'rails:db_migrate'
#invoke :'rails:assets_precompile'
  今回はrailsアプリでなくDBも使っていないため上記2行をコメントアウトします
  コメントアウトしないとrakeファイルがない(No Rakefile found)と言われてデプロイに失敗します

■デプロイ先設定
cd /var/tmp
bundle exec mina setup
  設定に合わせた事前準備を先ほど設定ファイルに記載したデプロイ先に対して実行します

■デプロイ実行
cd /var/tmp
bundle exec mina deploy
ls /var/tmp/deploy/releases/1/
  にgitからcloneしたデータがデプロイされていると思います

■Tips
・mina tasks
実行できるタスクの一覧を表示してくれます
・gem install mina --no-rdoc --no-ri
・mina init
上記の手順でもインストールと初期設定ができるみたいなんだけどGemfileがないためCould not locate Gemfileとなって怒られてしまいます
・デプロイ先ホストへのSSHノンパス設定
今回は同一ホストに対して実施しましたがデプロイ先のサーバに対してはSSHのノンパスログインを設定する必要があります
公開鍵を使ったSSHのノンパス設定方法は以下を参照してください
http://kakakikikeke.blogspot.jp/2012/03/10ssh.html
・cap shell的なコマンド
capistranoでいうところの「cap shell」みたいなコマンドはあるのか気になりました
mina console というタスクがデフォルトで用意されていたのでそれかなと思い実行したので裏側はrails console を実行していたので違うっぽい(?、未検証)
⇒mina run["ls -la"]といった感じで実行するとcap shellと同じようなことができるようです

■参考サイト

今回はデフォルトのレシピを使ってデプロイしてましたが次回は独自のレシピを使って実行してみたいと思います

2014年1月10日金曜日

SeleniumでJavaのテストコードを書く方法

私のおすすめの方法を紹介します

SeleniumでUIのテストを作成するときに個人的に一番大変だなと思うのがHTML要素の特定です
Selenium的にベストなのはすべての要素にid属性がふられているのがベストなのですが
そんなSeleniumに特化したUIは存在しないと思います
要素を特定するのに最悪は自然言語から検索してそれらを囲う要素を取得するなんてことをしなければならないこともあるのではと思います
今回は基本的な進め方の1つとして私がいつもやっているテストの作り方を紹介します
また今回はGoogleで検索するまでの動作を自動化してみたいと思います

■環境
Mac OSX 10.8.5(Windows環境でも問題なく動作します)
SeleniumIDE 2.5.0
Eclipse Juno(4.2.1)
Java SE 1.7.0_40

■UIテスト作成方法
0. 事前準備
  • SeleniumIDEのインストール
  • Eclipseのインストール(Maven Integration for Eclipse, TestNG for Eclipse プラグインのインストール)
  • Javaのインストール
  • (mavenのインストール)
は事前に済ませておいてください
mavenはなくてもOKです

1. SeleniumIDEでベースとなるテストの作成
  1. SeleniumIDEを起動します
  2. https://www.google.co.jp/ にアクセスします
  3. SeleniumIDEの右上のブラウザの操作記録を取得することができる赤ボタンをクリックし記録を開始します
  4. Googleの検索窓で「モバイルバックエンド」と入力しGoogle検索を押下します(今回は左記の動作を記憶しテストします)
  5. 記録を停止するため先ほどクリックした赤ボタンを押して記録を停止します
  6. 最終的にSeleniumIDE上で以下のようになっていればOKです


テストがうまく動作するかIDE上で確認します
Googleのトップに戻りSeleniumIDEの「現在のテストケースを実行」を押下します(緑色の三角のアイコンです)
ワードが自動で入力され検索結果が表示されればテストが実行されていることになります
IDE上にも問題がある場合はエラーが表示されるのでエラーがないことを確認します

2. テストのエクスポート
先ほど作成したテストをJavaのコードとしてエクスポートします
SeleniumIDEのメニューバーから
ファイル->テストケースをエクスポート->Java TestNG / Remote Control
をクリックします、ファイルの名前をつけて保存するダイアログが出力されますので「UITest.java」名前をつけて適当なフォルダ配下に保存します

おそらくは以下のようなコードが生成されると思いますので
package com.example.tests;

import com.thoughtworks.selenium.*;
import org.testng.annotations.*;
import static org.testng.Assert.*;
import java.util.regex.Pattern;

public class UITest extends SeleneseTestNgHelper {
        @Test public void testUI() throws Exception {
                selenium.open("/");
                selenium.type("id=lst-ib", "モバイルバックエンド");
                selenium.click("name=btnK");
        }
}
以下のようにいきなり変更します
ポイントしてはFirefoxDriverを生成してから要素を特定するようにしているのと
デフォルトで継承されているSeleneseTestNgHelperを継承しないようにしています
あとはtestngの機能を使ってテストの前処理と後処理を追加してあげました
※このあたりは個人的な好みの問題もあるのでSeleneseTestNgHelperを使って記載したい場合はそのままテストコードを使っても問題ないかと思います
package com.example.tests;

import org.openqa.selenium.By;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxProfile;
import org.testng.annotations.*;

import java.io.IOException;

public class UITest {

        private FirefoxDriver driver;

        @BeforeClass
        public void before() throws IOException {
                FirefoxProfile profile = new FirefoxProfile();
                profile.setAcceptUntrustedCertificates(true);
                driver = new FirefoxDriver(profile);
        }

        @Test
        public void open() {
                driver.get("https://www.google.co.jp");
        }

        @Test(dependsOnMethods={"open"})
        public void testUI() throws Exception {
                driver.findElement(By.id("lst-ib")).sendKeys("モバイルバックエンド");
                driver.findElement(By.name("btnK")).click();
        }

        @AfterClass
        public void after() {
                driver.close();
        }
}
コードが修正できたらeclipseにコードをコピペします
プロジェクトは通常のJavaプロジェクトでも問題ないですが、自分はmavenプロジェクトで作成しました
パッケージ名はSeleniumIDEからエクスポートしたデフォルトの場合com.example.testsになっているのでそれに合わせるといいと思います
mavenプロジェクトで作成した場合は必要なライブラリの定義(testngとselenium)をpom.xmlに記載し、通常のJavaプロジェクトで作成した場合は各種必要なJarファイルを公式サイト等から持ってくる必要があります
最終的にはプロジェクトが以下のような感じになっていればOKです

3. SeleniumIDEによる要素の特定とコード変換
更にテストを追加していきます
その際に今度は要素を特定しつつ直接コードを書いていくことにします
要素の特定方法自体はFirebugを使ったりするなどいろいろな方法があるかとは思いますが今回はSeleniumIDEだけで要素を特定する方法を紹介します

先ほどのGoogle検索から更に検索結果をクリックするテストを追加したいと思います
SeleniumIDEを開いた状態でGoogleの検索結果のページを開きます
SeleniumIDEでブラウザの記録を開始する赤ボタンをクリックします
その状態で検索結果の一番上のリンクをクリックします


するとSeleniumIDEの対象の部分にSeleniumIDEが解釈した要素情報が表示されます


この情報をコードに反映します
今回SeleniumIDEは対象を「link=ニフティクラウド mobile backend: アプリ開発をよりスマートに、スピーディに」と判断しました
これをコードに変換すると
driver.findElement(By.linkText("ニフティクラウド mobile backend: アプリ開発をよりスマートに、スピーディに")).click();
となります
今回、対象に表示されたのは「link=hogehoge...」の形式でしたが他にも「xpath=」や「css=」等が存在します
それぞれソースコードにひもづけることができ
  • xpath= ⇛ driver.findElement(By.xpath("ここに=以降の値を入力する")).click();
  • css= ⇛ driver.findElement(By.cssSelector("ここに=以降の値を入力する")).click();
  • id= ⇛ driver.findElement(By.id("ここに=以降の値を入力する")).click();
  • name= ⇛ driver.findElement(By.name("ここに=以降の値を入力する")).click();
といった感じで紐付けることができます(上記以外でも他にパターンはあるかと思います)
対象に表示されている内容はSeleniumIDEが現在表示しているページ上で一意に特定できる値を表示してくれますのでこれで簡単に一意の要素を特定することができます
ただ、ここで注意が必要なのがSeleniumIDEが一意と判断したのはあくまでも現在表示されているページ内なので動的にページの内容が変わる場合は同じ要素でも一意に特定できる要素が変わってくる場合があるのでそこはプログラム上でうまくカバーしてあげる必要があります
例えばif文を使って要素がなかった場合は他の要素でクリックするようにするなどです

4. 実行
プログラムに反映したらプログラムからテストできるか確かめてみます
今回はtestng形式で記載しているのでeclipseに「TestNG for Eclipse」のプラグインをインストールする必要があります
プラグインがインストールできたらUITest.javaを右クリックし「Run As」->「TestNG Test」をクリックし実行します
ブラウザが立ち上がり検索結果のページをクリックできたら成功です

うまくテストが成功しない場合はThread.sleepを入れてみたりしてください
あとはプログラム側でいかにうまくテストさせるかとなります

紹介は以上となります
あとは3, 4を繰り返し、要素を調べつつコードを作成し実行する感じが簡単なんじゃないかなと思います
ただ、SeleniumIDE上で特定できてもコードからだとうまく実行できないといったケースもあるようなのでその場合はコード側でループを利用しリトライ処理を実装する等の工夫が必要となります
UIのテストは作るのだけでも一苦労ですが、できたときに動いたときは普通の単体テストとはまた別の感覚が味わえるんじゃないかなと思います

2014年1月9日木曜日

Android API開発用メモ

Android APIについての自分用のメモ
わかったことや気づいたことTipsがあったら随時追記していく

■基本的なソースコードの書き方
  1. activity_main.xml でUIを作成する
  2. Layoutsからレイアウトを決めてレイアウト上にButtonやEditTextを配置していく
  3. Activity側のクラスでtextView = (TextView) findViewById(R.id.textView1);的な感じでUIのオブジェクトを取得して操作する
  4. Buttonを押下した際のアクションはactivety_main.xmlのPropertiesで「On Click」に呼び出すメソッド名を記載すればOK、メソッドはpublic voidで引数にViewのオブジェクトを受け付けるようにすればOK
  5. メソッドを定義するのはActivityを継承しているクラス内で定義する(定義はActivityを継承しているクラスでして実際のロジックは別パッケージの別クラスでもOK

■Activity
画面そのもの
・使い方
public class MainActivity extends Activity {
・・・
}
スマートフォンが画面をスライドさせて次の画面にいくと次のActivityになる、そんなイメージ
Activityには状態があります

活動中や停止中、一時停止などがありそれぞれの状態にあわせて処理を実行することができる
そして使い終わったActivityはonDestroyしないと端末のメモリを逼迫することになるのでちゃんと破棄するようにしよう
(Javaのファイル処理やストリーム処理と同じで終わったオブジェクトはcloseしないといけないということです)

※本家Androidのサイトから拝借

■Intent
Activityを遷移するためのクラス
・使い方
Intent intent = new Intent(this, MeiroActivity.class);
startActivity(intent);

■SharedPreferences
画面間データの受け渡しをするためのKey, Valueのマップ
・使い方
SharedPreferences sharedPref = getSharedPreferences(key, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putString("key1", "value1");
データを保存する場合はSharedPreferencesオブジェクトからEditorオブジェクトを作成し、そのオブジェクトに保存
データを取得する場合はSharedPreferencesから直接取得する

SharedPreferencesオブジェクトを使用する場合は、それぞれのActivity内でオブジェクトを作成する

データ形式はKey, Valueなので無限に保存することができるが揮発性がありアプリが終了するとデータが失われてしまう
一時的なデータの受け渡しに使う(DBとしては使用しない)

■File
Android内部にファイルを生成し永続的にデータを保存
・使い方
FileOutputStream fos = openFileOutput(filename, Context.MODE_APPEND);
OutputStreamWriter osw = new OutputStreamWriter(fos);
BufferedWriter bw = new BufferedWriter(osw);

FileInputStream is = openFileInput(filename);
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
Android独自のopenFileOutputとopenFileInputを使用してオブジェクトを取得したあとに通常のjava.io系のクラスを使えばファイル操作ができます

■DB
Android内部にSqliteDBを作成し永続的にデータを保存
・使い方
DatatradeDBAdapter ddba = new DatatradeDBAdapter(getApplicationContext());
SQLiteDatabase db = ddba.getReadableDatabase();
作成された上記のdbオブジェクトに対してinsertやselect等のSQLを発行することが可能です
DatatradeDBAdapterは独自で作成したクラスで、SQLiteOpenHelperを継承して作成します
DatatradeDBAdapterではテーブルの作成や削除を行うことができるSQLを管理したメソッドを持っています
DBのスキーマを文字列で管理するスキーマクラスを別途作成しておき、それをActivety等で呼び出すようにしましょう
もちろんDatatradeDBAdapter内のCreate Table内でもスキーマクラスの文字列を使ってテーブル作成します

■SurfaceView
ゲームやアニメーションを多用する滑らかに表示するためのView
・使い方
public class MeiroActivity extends Activity implements SurfaceHolder.Callback {

 private SurfaceHolder sh;

 protected void onCreate(Bundle savedInstanceState) {
  ・・・
  SurfaceView sv = (SurfaceView) findViewById(R.id.surfaceView1);
  sh = sv.getHolder();
  sh.addCallback(this);
 }

 public void surfaceCreated(SurfaceHolder holder) {
  drawTest();
 }

 public void drawTest () {
     Canvas canvas = sh.lockCanvas();
     ・・・
     sh.unlockCanvasAndPost(canvas);
 }

}
SurfaceHolderはonCreateで作成し必ずaddCallbackしましょう
Canvasの作成は別メソッドで実施し、surfaceCreatedメソッド内で実際に描画する処理を呼び出します
でないと、Canvasオブジェクトが内部的に作成されてないためにNullPointerExceptionが発生します
http://stackoverflow.com/questions/9510792/lock-canvas-fail-and-canvas-is-null-when-useing-surfaceview

■ImageButton
ボタンに画像を使用することができます
・使い方

ボタンを作成するだけならXMLのみで可能です
何かアクションを実行したい場合にはonClick等を設定し、Java側のソースコードにメソッドを作成するだけでボタンを押下したときに自動でそのメソッドが実行されます
普通のButtonオブジェクトのように作成可能です

■Context
http://individualmemo.blog104.fc2.com/blog-entry-41.html

■Theme.NoTitleBar
Activity上部のタイトルを削除することが可能
・使い方
android:theme=@android:style/Theme.NoTitleBar"
テーマはAndroidManifest.xmlに記載します

■Camera
カメラ機能、インカメラとアウトカメラが使える
・使い方
どうやらエミュレータではカメラ機能は使えないようです
ただ、PCにWebCameraの機能があればそれと連携してカメラを使うことはできるようです
http://wiki.usagee.co.jp/android/%E3%82%A8%E3%83%9F%E3%83%A5%E3%83%AC%E3%83%BC%E3%82%BF%E3%83%BC%E3%81%A7%E3%82%AB%E3%83%A1%E3%83%A9%E3%82%92%E4%BD%BF%E3%81%86%E6%96%B9%E6%B3%95

■ArrayAdapter
ListViewにTextView以外を表示する、カスタムする場合に使用する
・使い方
public class ProductListAdapter extends ArrayAdapter<Cell> {

  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
    ...
  }

}
ArrayAdapterを継承した独自Adapterクラスを実装する
ジェネリックの型も自分でPOJOとなるクラス(上記の場合はCellクラス)を作成する
Cellクラスは各ListViewの項目に表示するスタイルを定義したクラスにする
また、cell.xmlみたいな感じでUIも作成する
getViewを必ずOverrideする、getViewは各項目が表示されるたびにコールされる
このgetView内でcell.xmlからオブジェクトを作成して各項目を表示する

■AsyncTask
Androidで非同期を実現するためのクラス、AndroidではUIの描画とHTTP通信などは別スレッドにする必要がある
・使い方
public class AsyncImageViewSetter extends AsyncTask<String, Void, Interger> {

  @Override
  protected Integer doInBackground(String... params) {
    ...
  }

  @Override
  protected void onPostExecute(Integer value){
    ...
  }

}
・呼び出し元
AsyncImageViewSetter image = new AsyncImageViewSetter(str);
image.execute();

AsyncTaskを継承したクラスを作成する
ジェネリックの<String, Void, Integer>はそれぞれ「非同期処理に渡せる型、進行中に更新する値の型、非同期処理終了後に渡す型」になります
doInBackgroundが実際に非同期する部分で上記のジェネリックだとStringの値が配列で渡されます
doInBackgroundが終了すると自動的にonPostExecuteが呼ばれdoInBackgroundの戻り値が渡ってきます
基本的にはUIへの操作はこのonPostExecuteで実施します
他にもonProgressUpdateというメソッドを実装すると非同期の実行中に状況を確認したりすることができます

2014年1月8日水曜日

【maven】getClass().getResourceAsStream("hogehoge.properties")でNullPointerException

target/test-classes/com/hogehoge.properties

にプロパティファイルを配置する必要がありました
src配下にいくら置いても実際にコンパイルした.classファイルはtarget配下にできるのでプロパティファイルもtarget配下にコピーしてやる必要があります
mavenでは自動でコピーしてくれる機能が備わっており

src/test/resources
に必要なプロパティファイルを置いておくと「target/test-classes」に自動でコピーし
src/main/resources
に必要なプロパティファイルを置いておくと「target/classes」に自動でコピーしてくれます

また、resources配下にディレクトリも作成しておくとそのディレクトリ階層に合わせてプロパティファイルをコピーしてくれます

pom.xmlには特にpluginの設定を書く必要はありません
内部的にはmaven-resources-pluginを使用しているようですが、デフォルトで使用できるのでフォルダを作成してプロパティファイルを配置すれば自動でコピーしてくれます

2014年1月7日火曜日

FirefoxにSeleniumIDEをインストールする方法

■環境
Windows7 64bit
Firefox 26.0
SeleniumIDE 2.5
※インストール方法に関しては2014/01/07時点でのバージョンとインストール方法です

■インストール方法
1. 公式のダウンロードサイトにアクセス
http://docs.seleniumhq.org/download/
firefoxのプラグインの拡張子である「.xpi」ファイルを直接ダウンロードできるリンクがあるのでクリックします


クリックすると
このサイトからはFirefoxにソフトウェアをインストールできない設定になっています
という吹き出しが左上にでるので「許可」をクリックしプラグインをインストールします
許可をクリック後、更にダイアログでインストールするかを尋ねられるので「今すぐインストール」をクリックします


2. Firefoxを再起動
インストールが完了後に左上の吹き出しから再起動を要求されるので再起動を実施します
再起動後に
メニューバー -> ツール -> SeleniumIDE
があればインストールに成功しています


3. 起動確認
先ほどのメニューバーからSeleniumIDEをクリックし以下のようなUIが起動されればOKです


これでインストールは完了です
せっかくインストールしたので実際にUIをテストする方法も紹介したいなと思っています

2014年1月6日月曜日

【ruby】'encode': "\x85" followed by "\"" on Windows-31J (Encoding::InvalidByteSequenceError)

ruby自体のファイルの文字コードとマジックコメントはUTF-8とします
rubyコマンドを実行する際に文字コードを指定しないと実行しているターミナルのデフォルトの文字コードが使われてしまいうまくマルチバイト文字が処理されないようです

ruby -Eutf-8 sample.rb

環境は以下のとおり
  • Windows7 64bit
  • ruby 2.0.0p195 (2013-05-14) [x64-mingw32]