タイトル/名前 | 更新者 | 更新日 |
---|---|---|
日記/2010/10/14/System.exit(anyInt())をmockする!! | msakamoto-sf | 2010-10-14 13:06:24 |
技術/MacOSX/FinderとTerminalのPATH設定 | msakamoto-sf | 2010-10-13 17:23:49 |
Java/JUnit | msakamoto-sf | 2010-10-13 17:21:17 |
Java/JUnit/"test"接頭辞無しの"@Test"メソッドが認識されない | msakamoto-sf | 2010-10-13 17:18:35 |
Java/Property, ResourceBundleメモ | msakamoto-sf | 2010-10-06 11:51:06 |
Java/Collections/Setの同期化メモ | msakamoto-sf | 2010-10-05 15:27:17 |
Java/Apache MINA/Echo Server, Client 習作 | msakamoto-sf | 2010-10-04 21:08:05 |
画像/2010/10/04/210546/WS000222.jpg | msakamoto-sf | 2010-10-04 21:05:57 |
画像/2010/10/04/172303/WS000221.jpg | msakamoto-sf | 2010-10-04 17:23:14 |
画像/2010/10/04/172220/WS000220.jpg | msakamoto-sf | 2010-10-04 17:22:45 |
public class Foo { public void shutdown() { System.exit(0); } }
Fooクラスのshutdown()メソッドで、以下の挙動をassertする!!
テストケース:
@RunWith(PowerMockRunner.class) @PrepareForTest( { Foo.class }) public class FooTest extends TestCase { @Test public void shutdown() { PowerMockito.mockStatic(System.class); Foo f = new Foo(); f.shutdown(); PowerMockito.verifyStatic(Mockito.times(1)); System.exit(0); } }
これでOK。
色々試行錯誤したのだけど、"void"メソッドだからと
PowerMockito.doNothing().when(System.class);
してみたが、これ入れると以下のような例外が出てしまい、手詰まりに。
org.mockito.exceptions.misusing.UnfinishedStubbingException: Unfinished stubbing detected here: -> at org.powermock.api.mockito.internal.PowerMockitoCore.doAnswer(PowerMockitoCore.java:31) E.g. thenReturn() may be missing. Examples of correct stubbing: when(mock.isOk()).thenReturn(true); when(mock.isOk()).thenThrow(exception); doThrow(exception).when(mock).someVoidMethod(); Hints: 1. missing thenReturn() 2. although stubbed methods may return mocks, you cannot inline mock creation (mock()) call inside a thenReturn method (see issue 53)
外したら動いた。この辺が曖昧なのが多少気持ち悪いが、とりあえず前進を再開できる。
参考メモ:
Hotな話題だけに、トピック記事もsampleも出揃いつつあるのだけれど、中々"Spot On!"な回答/sampleが無くてやきもきした。
FinderとTerminalとでPATH設定が異なる。外部プログラムを起動するアプリケーションをTerminal経由とFinder経由、両方で動かす時はPATH設定に注意すること。
参考リンク:
JUnit関連参考リンク:
JUnit 4.8.1 使用中に、
public class FooTest extends TestCase { @Test public void foo { assertTrue(true); } }
がElipse/Maven双方から認識されない現象に遭遇したのでメモ。
環境:
JUnit : 4.8.1 POM : <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.8.1</version> <scope>test</scope> </dependency> Maven : 2.2.1 Eclipse : 3.5 Galileo org.eclipse.jdt.junit (3.5.1.r351_v20090708-0800) org.eclipse.jdt.junit.runtime (3.4.100.v20090513-2000) org.eclipse.jdt.junit4.runtime (1.1.0.v20090513-2000) org.eclipse.pde.junit.runtime (3.4.0.v20090527) org.junit (3.8.2.v20090203-1005) org.junit4 (4.5.0.v20090824) JDK 1.6.12 Windows XP SP2 / 2GB RAM / Pen4 HT(全て表示する)
書く度に忘れてしまうので、まとめてメモ。
Test1.java:
import java.io.*; import java.util.*; public class Test1 { public static void main(String args[]) { if (1 > args.length) { System.err.println("args : *.properties"); return; } Properties prop = new Properties(); try { prop.load(new FileInputStream(new File(args[0]))); } catch (IOException e) { e.printStackTrace(); return; } for (Map.Entry<Object, Object> e : prop.entrySet()) { System.out.println(e.getKey() + "=" + e.getValue()); } } }
foo.ini
## java property file a.b.c=123 #comment foo=aaaaaaaaaaaaaaaaaaaaaaaa
実行:
> java Test1 foo.ini a.b.c=123 foo=aaaaaaaaaaaaaaaaaaaaaaaa
java.util.Propertiesは Map<Object,Object> interfaceを実装してるので、アプリ中ではinterfaceで受けて使い回してもOK。
参考:
ただのResourceBundleだけだと、リソースバンドルをクラスで作ったり面倒くさい。
メッセージの国際化なら文字列しか扱わないので、PropertyResourceBundleでpropertiesファイルを使って済ませることが出来る。
pack/age/Test1.java:
package pack.age; import java.util.*; public class Test1 { public static void main(String args[]) { Locale currentLocale = Locale.getDefault(); System.out.println(currentLocale); ResourceBundle res = PropertyResourceBundle.getBundle( "pack.age.restest"); System.out.println(res.getString("foo")); System.out.println(res.getString("bar")); } }
pack/age/restest.properties:
foo=English bar=Version
pack/age/restest_cp932.properties:
foo=日本語 bar=バージョン
UNICODEに変換:
native2ascii pack\age\restest_cp932.properties pack\age\restest_ja.properties
日本語ロケールで実行:
> java -cp . pack.age.Test1 ja_JP 日本語 バージョン
英語ロケールで実行1:
> java -Duser.language=en -cp . pack.age.Test1 en_JP English Version
国がJPなのに言語がenって、何か不思議ですね。
英語ロケールで実行2:
> java -Duser.language=en -Duser.region=us -cp . pack.age.Test1 en_US English Version
参考:
Set操作、特に変更操作と拡張for構文を使ったiterationを別スレッドで処理する時のメモ。
int値フィールド一つだけの簡単なSomeObjectをSetにaddするWriteThreadと、Setから拡張for構文でiterationし、int値の合計を表示するEnumThreadの二つを起動する。
SetTest1.java:
import java.util.*; import java.util.concurrent.*; public class SetTest1 { final Set<SomeObject> objects = Collections.synchronizedSet(new HashSet<SomeObject>()); Random rnd = new Random(); public static void main(String args[]) { new SetTest1().test(); } public void test() { new EnumThread().start(); new WriteThread().start(); } class SomeObject { int val; public SomeObject(int val_) { val = val_; } public int getVal() { return val; } } class EnumThread extends Thread { public void run() { while (true) { long sum = 0; for (SomeObject o : objects) { sum += o.getVal(); } System.out.println("sum = " + sum); try { sleep(100); } catch (InterruptedException e) { } } } } class WriteThread extends Thread { public void run() { while (true) { objects.add(new SomeObject(rnd.nextInt(10))); try { sleep(10); } catch (InterruptedException e) { } } } } }
Collections.synchronizedSet()はSetに対する操作を同期化してくれるが、iteration経由での操作については対象外。
実行してみると、かなり早いタイミングで java.util.ConcurrentModificationException が発生してしまう。
> javac SetTest1.java > java SetTest1 sum = 0 sum = 48 sum = 78 sum = 128 sum = 175 sum = 217 Exception in thread "Thread-0" java.util.ConcurrentModificationException at java.util.HashMap$HashIterator.nextEntry(HashMap.java:793) at java.util.HashMap$KeyIterator.next(HashMap.java:828) at SetTest1$EnumThread.run(SetTest1.java:35)
java.util.Collections.synchronizedSet()のJavaDocにも記載されているが、iteratorを使う時は手動でコレクションに対するsynchronized()で囲ってみる。
SetTest2.java:
// ... class EnumThread extends Thread { public void run() { while (true) { long sum = 0; synchronized(objects) { for (SomeObject o : objects) { sum += o.getVal(); } } System.out.println("sum = " + sum); try { sleep(100); } catch (InterruptedException e) { } } } } // ...
これであれば、ConcurrentModificationException 例外は発生しない。
複数スレッドからの操作で、全体として変更操作よりもiteration操作の方が明らかに多い場合は java.util.concurrent.CopyOnWriteArraySet<E> を使ってみる。iteration中の別スレッドからの変更(追加/削除)は、配列をコピーして行われる。変更操作はiteratorに反映されないため、iteratorは"スナップショット"として使うことになる。iteratorに対する要素変更はサポートされず、実行するとUnsupportedOperationExceptionがthrowされる。
//... (SetTest1.javaと同じ) public class SetTest3 { final Set<SomeObject> objects = new CopyOnWriteArraySet<SomeObject>(); Random rnd = new Random(); public static void main(String args[]) { new SetTest3().test(); } //... (SetTest1.javaと同じ)
手動でsynchronized()で囲うよりは、CopyOnWriteの方が個人的には好み。
Apache MINA で Echo Server/Client を作ってみた。
通常、習作のechoサーバ/クライアントと言えばせいぜいが数文字のテキストデータをやりとりする程度。
今回の "act8" は可変長パケットっぽいデータ形式にラップして、1MBのランダムデータを複数クライアントから延々と送受信し続ける状況を想定している。
目的としては、実際に1MBを越えるデータを送受信するアプリケーションをMINAで組んだ場合、ByteBufferを使うことでヒープオーバーフローやOutOfMemoryが発生するか検証すること。
実際にServer側で
-server -Xms200m -Xmx200m -Xloggc:gc.log
でgcログを出力し、gcviewerで確認した結果が以下のスクリーンショット。クライアント側は10個の接続、1MBのパケットを100ms間隔で送受信している。
青い線がヒープ消費量、灰色の縦線がGC発生とGCにかかった時間を示す。
なんとかオーバーフローやOutOfMemoryが発生せずに動き続けているが、10秒強の間隔でフルGCが発生している。
但し上記例だと、クライアント側が1秒間に送信するデータの合計は
1MB * 10(1000/100ms) * 10 = 100MB
で、800Mbpsになる。実験はloopbackデバイスを使っているとはいえ、現実的には100Mbpsのネットワークカードを使う事になるので、明らかに送信過剰といえる。
そこで、実際の100Mbpsに即したパケットサイズに調整してみる。間隔もクライアント側でビジネスロジックを処理する事を考慮し、200ms位を目安にしてみる。
10クライアント x 200ms(interval) x 262144bytes = 104,857,600 bits/s (100Mbps)
実際はこれにTCP/IPのヘッダーが付く為、100Mbpsを少し上回る計算になる。
クライアント側を上記セッティングで、サーバー側のヒープMAXを200MB、上記と同じ条件にして実行したのが下記gcviewerになる。
フルGCは発動せず、かなり安定したヒープ使用量とGC状況になった。
検証環境
Windows XP SP3 Pentium4 2.8GHz (HT) 2GB RAM Java 1.6.12 Apache MINA 1.1.7