EasyMockに触れる

EasyMockを使用するとモック(擬似)オブジェクトを使用したテストを行うことが
できるらしい。
また、モック(擬似)オブジェクトの目的は、インターフェースの使われ方をテストすることであり、スタブと混同するのは正しくないとのこと。ふーん。
①スタブ・・・例えば、データベース接続を伴うデータの取得処理を隠蔽し、DBの代わりに配列などでDBをシュミレートする。
また、アプリケーションの実行、ビルドを軽量化する。
②モック・・・API(インターフェース)が正しく呼び出されることを確認する。

でも、いまいち違いがピンと来ない。。。
EasyMockでも①でいうところのDBのシュミレートができそうだし・・・。

EasyMockのインストール

最新バージョン(今回はeasymock2.0)をEasyMockから取得する。
ちなみにeasymock2.0はJDK5.0が必要です。

EasyMockを使用したテストの手順

①モックオブジェクトの生成
②正常時の期待値の設定
 ※.モックオブジェクトへ振る舞いを記録する。
③モックテストの実施
④モックテストの検証
 ※.モックオブジェクトが記録した振る舞い通りに呼び出されたか検証する。

サンプルプログラム

テスト対象のプログラム

A・・・わざとResultSet#closeメソッドの呼び出しをコメントアウトしています。

package sample.easymock;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Map;

public class SampleDAO {
	public Map selectSample(Connection con, String sql) throws SQLException {
		Map result = null;
		Statement st = con.createStatement();
		ResultSet rs = st.executeQuery(sql);
		if(rs.next()) {
			result = new HashMap();
			result.put("sample", rs.getString(1));
		}
//		close()の呼び出しをコメントアウト	//A	
//		rs.close();
		st.close();
		return result;
	}
}
テストプログラム

B・・・verify(rsMock) の呼び出しでAssertionErrorがthrowされる

package sample.easymock;

import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;

import junit.framework.TestCase;

public class SampleDAOTest extends TestCase {

	private Connection conMock;
    private Statement stMock;
	private ResultSet rsMock;

	protected void setUp() {
		// ①Mockオブジェクトの生成
		conMock = createMock(Connection.class);
		stMock  = createMock(Statement.class);
		rsMock  = createMock(ResultSet.class);
    }
	/*
	 * Test method for 'sample.easymock.SampleDAO.selectSample(Connection, String)'
	 */
	public void testSelectSample() throws Exception {
		
		// ②期待値の設定
		expect(conMock.createStatement()).andReturn(stMock);
		expect(stMock.executeQuery("select * from sample")).andReturn(rsMock);
		expect(rsMock.next()).andReturn(false);

		rsMock.close();
		stMock.close();
		
		replay(conMock);
		replay(stMock);
		replay(rsMock);
		
		// ③モックテストの実施
		SampleDAO dao = new SampleDAO();
		dao.selectSample(conMock, "select * from sample");
		
		// ④モックテストの検証
		verify(conMock);
		verify(stMock);
		verify(rsMock); // B

	}
}

使ってみた感想としては、簡単に動的なモックオブジェクト(スタブと呼びたい・・)を
作成できてなかなか便利。
andReturnメソッドをうまく使えばやはりスタブとして利用できそう(^^;


複数回呼び出しの確認(expectLastCall().times(3))
テスト対象のプログラム
		if(rs.next()) {
			result = new HashMap();
			result.put("sample1", rs.getString(1));
			result.put("sample2", rs.getString(1));
			result.put("sample3", rs.getString(1));
		}
テストプログラム
		expect(conMock.createStatement()).andReturn(stMock);
		expect(stMock.executeQuery("select * from sample")).andReturn(rsMock);
		expect(rsMock.next()).andReturn(true);
		expect(rsMock.getString(1)).andReturn("hello");
		expectLastCall().times(3);

Swatchのインストール(ログだけ)

yumリポジトリファイルを追加

vi /etc/yum.repos.d/dag.repo

[dag]
name=Dag RPM Repository for Fedora Core
baseurl=http://apt.sw.be/fedora/$releasever/en/$basearch/dag
gpgcheck=1
enabled=1

yumを使用してインストール

[root@fourth tmp]# yum install swatch
Setting up Install Process
Setting up Repos
dag                       100% |=========================| 1.1 kB    00:00
base                      100% |=========================| 1.1 kB    00:00
updates-released          100% |=========================|  951 B    00:00
Reading repository metadata in from local files
dag       : ################################################## 2860/2860
base      : ################################################## 2622/2622
updates-re: ################################################## 1020/1020
Resolving Dependencies
--> Populating transaction set with selected packages. Please wait.
---> Package swatch.noarch 0:3.1-1.1.fc3.rf set to be updated
--> Running transaction check
--> Processing Dependency: perl(Date::Calc) for package: swatch
--> Processing Dependency: perl(Time::HiRes) for package: swatch
--> Processing Dependency: perl(Date::Format) for package: swatch
--> Processing Dependency: perl(Date::Parse) for package: swatch
--> Processing Dependency: perl(Mail::Sendmail) for package: swatch
--> Processing Dependency: perl(File::Tail) for package: swatch
--> Restarting Dependency Resolution with new changes.
--> Populating transaction set with selected packages. Please wait.
---> Package perl-File-Tail.noarch 0:0.99.1-1.1.fc3.rf set to be updated
---> Package perl-Mail-Sendmail.noarch 0:0.79-1.1.fc3.rf set to be updated
---> Package perl-Time-HiRes.i386 0:1.55-3 set to be updated
---> Package perl-TimeDate.noarch 1:1.16-2 set to be updated
---> Package perl-Date-Calc.i386 0:5.3-9 set to be updated
--> Running transaction check
--> Processing Dependency: perl(Bit::Vector) for package: perl-Date-Calc
--> Restarting Dependency Resolution with new changes.
--> Populating transaction set with selected packages. Please wait.
---> Package perl-Bit-Vector.i386 0:6.3-3 set to be updated
--> Running transaction check

Dependencies Resolved
Transaction Listing:
  Install: swatch.noarch 0:3.1-1.1.fc3.rf - dag

Performing the following to resolve dependencies:
  Install: perl-Bit-Vector.i386 0:6.3-3 - base
  Install: perl-Date-Calc.i386 0:5.3-9 - base
  Install: perl-File-Tail.noarch 0:0.99.1-1.1.fc3.rf - dag
  Install: perl-Mail-Sendmail.noarch 0:0.79-1.1.fc3.rf - dag
  Install: perl-Time-HiRes.i386 0:1.55-3 - base
  Install: perl-TimeDate.noarch 1:1.16-2 - base
Total download size: 482 k
Is this ok [y/N]: y
Downloading Packages:
(1/4): perl-File-Tail-0.9 100% |=========================|  21 kB    00:00
(2/4): swatch-3.1-1.1.fc3 100% |=========================|  45 kB    00:00
(3/4): perl-Mail-Sendmail 100% |=========================|  23 kB    00:00
(4/4): perl-TimeDate-1.16 100% |=========================|  31 kB    00:08
Running Transaction Test
Finished Transaction Test
Transaction Test Succeeded
Running Transaction
Installing: perl-Time-HiRes 100 % done 1/7
Installing: perl-File-Tail 100 % done 2/7
Installing: perl-TimeDate 100 % done 3/7
Installing: perl-Mail-Sendmail 100 % done 4/7
Installing: perl-Date-Calc 100 % done 5/7
Installing: swatch 100 % done 6/7
Installing: perl-Bit-Vector 100 % done 7/7

Installed: swatch.noarch 0:3.1-1.1.fc3.rf
Dependency Installed: perl-Bit-Vector.i386 0:6.3-3 perl-Date-Calc.i386 0:5.3-9 perl-File-Tail.noarch 0:0.99.1-1.1.fc3.rf perl-Mail-Sendmail.noarch 0:0.79-1.1.fc3.rf perl-Time-HiRes.i386 0:1.55-3 perl-TimeDate.noarch 1:1.16-2
Complete!
[root@fourth tmp]# mkdir /etc/swatch
[root@fourth tmp]# vi /etc/swatch/.swatchrc
watchfor /Priority\: 2/
echo=normal
mail=user hoge@hoge.jp, subject=Snort Security Alert!

ランレベル

Fedora Coreランレベル

ランレベルとは、Linuxの動作モードのことで、ランレベルごとに
起動されるプログラムは異なります。
Linuxでは、ランレベル別のディレクトリ(/etc/rc.d/rc?.d)にある
ファイル名により、各ランレベルで実行するプログラムを指定しています。

 例えば、ランレベル3の場合は/etc/rc.d/rc3.dにあるスクリプトのうち
S(Start)ではじまるスクリプトが実行され、K(Kill)で始まるスクリプト
は実行されません。
つまり、ファイル名の先頭1文字を変更することで起動するサービスを変更する
ことができるというわけです。
ちなみにKやSに続く数字は、実行されるスクリプトの優先順位をけってします。
(数字の小さいスクリプトから順に実行される。)

ランレベル 説明
0 停止
1 シングルユーザモード
2 マルチユーザモード(NFS*1なし)
3 マルチユーザモード
4 未使用
5 GUIログイン(X11
6 再起動

ランレベルの変更はinitコマンドで行います。
ランレベルを3にする場合

# init 3

現在のランレベルの確認はrunlevelコマンドで行います。

# runlevel
N 5

*1:UNIXシステムで利用されるファイル共有システム。

アクセス制限

サービスごとのアクセス制御

 TCP/IPのサービスへのアクセスを制限する方法としては、「tcp_wrappers」または「xined」に
よる制限が一般的です。

tcp_wrappers

 tcp_wrappersは、telnetdやftpdなどのサービスをインターネットデーモン「inetd」から起動
する場合に「/usr/sbin/tcpd」経由で起動し、「/etc/hosts.allow」および「/etc/hosts.deny」
ファイルでアクセス制限を行う為の仕組です。

xined

 Fedora Coreはデフォルトでindetdによるサービスの起動を行わず、代わりにxinetdを使用する
ようになっている。
xinedを使用したアクセス制限は、「/etc/xinetd.conf」ファイル、または「/etc/xinetd.d」
ディレクトリ以下のサービスごとのファイルに記述をする。

アプリケーションのアクセス制限

アプリケーションには独自のアクセス制限機能が実装されている。
 
例)

Apacheの場合

httpd.confによるアクセス制限の例

SetEnvIf User-Agent "DoCoMo" com_on
<Directory /var/www/html/>
〜(中略)〜
  Order deny,allow
  Deny from all
  Allow from example.jp
  Allow from 10.1.0.0/16
  Allow from 192.168.1
  Allow from env=com_on
</Directory>
BINDのacl

 BINDの場合、「named.conf」にアクセス制限を記述することが可能。
DNSサーバの場合、ゾーンデータを転送するスレーブサーバや問い合わせを許可するコンピュータ
やネットワークを限定しておくことが重要です。

acl "internal" {
  192.168.1/24;             ← 内部ネットワークのリスト
  192.168.2/24;
};

acl "aclzone" {
  10.1.xxx.xxx;             ← ゾーンデータを転送するスレーブサーバのリスト
  210.xxx.xxx.xxx;
};

options {
  directory "/var/named";
  allow-query { "internal" };     ← ①の内部のネットワークからの問い合わせにのみ回答する。
};

〜(中略)〜

zone "example.co.jp" {
  type master;
  file "example.co.jp.zone";
  allow-transfer { "aclzone"; };  ← ゾーンデータを転送するサーバを②のサーバに制限。
};