唐紅に水くくるとは

通年チョコミントを食べたい

ServletOutputStreamの取得

とあるテストケースで、
ServletOutputStream.writeを使った結果をassertしたくなりました。

ググってもググってもByteArrayOutputStream使えばいいよ!という答えしか出てこなくて
途方に暮れた。。。

が、assertの方法をちょっと変えることで回避。
ResponseWrapper#getResponseBody の結果のbyte[]でassertすることにした。
具体的にはこんな感じで

        // 外側のクラスをインスタンス化
        final SampleResponseWrapper target = new SampleResponseWrapper(res);
        // mockのresponseを使って、privateメソッドを呼び、outputStreamを詰め込む。
        target.getOutputStream();

        ServletOutputStream csrw = (ServletOutputStream) field.get(target);
        csrw.write(129);

        // 結果のbyte配列をint型に変える
        int intValue = 0;
        for (byte b : target.getResponseBody()) {
            intValue = b;
            if (intValue < 0) {
                intValue += 256;
            }
        }

        assertEquals(129, intValue);


テスト対象のクラスは、ざっくりこんな感じ

public class SampleResponseWrapper extends HttpServletResponseWrapper {

    /** 出力バッファ */
    private InnerClassOutputStream wrappedOut;

    /** 内部クラス */
    private static class InnerClassOutputStream extends ServletOutputStream {

        /** 内部クラスの出力バッファ */
        private ByteArrayOutputStream buffer;

        @Override
        public void write(int b) throws IOException {
            buffer.write(b);
        }
    }

}

SampleResponseWrapper は、HttpServletResponseWrapperを継承したクラス。
その中のフィールドに「wrappedOut」がいて、
その型はSampleResponseWrapper の内部クラス(private static)。

結局本質的なテストができてない気はムンムンしつつ、
思ったことは、
何でもかんでもprivateにしないで・・・ということ(自分が作ったクラスじゃないので)

いや、必要ならprivateでももちろん構わないけど、
単体テストカバレッジを見逃すとか、そういう配慮的な何かがほしかった。

テスト設計って、大事だなーって。
カバレッジ100%っていうのは管理上確認するのは簡単だけど、
本当にそのテストが品質を担保してるのか?っていったら微妙だと思う。
(むしろ、カバレッジを上げるのに時間を使ってしまって、
 単体テストケースを考えたりレビューしたりする時間が減るのではないかと。)

テストケース(しかも単体)なんて、もっと機械的にできてもいいのになぁ??と
思ってしまう自分でした。

log4jdbc+slf4jでJDBC周りのログを出す

タイトルまま。
log4jだけだとPrepareStatement.addBatch()のログが出ないので、
出せるように修正。

いいなと思ったのは、既存のlog4jの設定には手を加えなくてもいいこと。
(追記は必要。)

必要なライブラリ:
log4jdbc4-1.2.jar
slf4j-api-1.7.7.jar
slf4j-log4j12-1.7.7.jar
(mavenつかってればpom書けばいいんだけどなぁ…)

jdbc設定の修正
今はoracleを使ってるので、jdbcの設定は以下のような感じ。

driver=oracle.jdbc.OracleDriver
url=jdbc:oracle:thin:@host:1521/sid

それを、こう。

driver=net.sf.log4jdbc.DriverSpy
url=jdbc:log4jdbc:oracle:thin:@host:1521/sid

ドライバ名は、JDBC接続文字列から勝手に判定してくれてるのかな。
とくに設定する必要はありませんでした。

log4j.propertiesに以下を追記

log4j.logger.jdbc.audit=DEBUG, consoleLog
log4j.additivity.jdbc.audit=false
 
log4j.logger.jdbc.resultset=DEBUG, consoleLog
log4j.additivity.jdbc.resultset=false
 
log4j.logger.jdbc.sqlonly=DEBUG, consoleLog
log4j.additivity.jdbc.sqlonly=false
 
log4j.logger.jdbc.sqltiming=DEBUG, consoleLog
log4j.additivity.jdbc.sqltiming=false
 
log4j.logger.jdbc.connection=DEBUG, consoleLog
log4j.additivity.jdbc.connection=false
 
log4j.logger.log4jdbc.debug=OFF, consoleLog
log4j.additivity.log4jdbc.debug=false

出たログを見た感じ、今は[audit]と[sqltiming]があれば十分っぽい。
auditでPrepareStatementのログを出してくれて、
sqltimingで実際に発行するSQLを出してくれる。
なお、アプリケーションログの設定があれば、そちらも変わりなく出力される。

mavenでコンパイルするときのJDKバージョンの指定

mavenつかってコンパイルするときに
JDKのバージョンを明示的に指定したい。

つかうのはmaven-compiler-plugin

<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.3.2</version>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
					<testSource>${jdk.version}</testSource>
					<testTarget>${jdk.version}</testTarget>
					<encoding>${project.build.sourceEncoding}</encoding>
					<compilerArguments>
					    <verbose />
<!-- 					    <bootclasspath>${env.JAVA_HOME5}\jre\lib\rt.jar</bootclasspath> -->
<bootclasspath>C:\Program Files\Java\jdk1.8.0_20\jre\lib\rt.jar</bootclasspath>
<!--                     -noverify -->
					</compilerArguments>
				</configuration>
			</plugin>

source : 受け付けるソースコードのバージョン
target : ここに指定したバージョン以降で動作することを保証する
compilerVersion:Version of the compiler to use()

いろいろためしてみよう。

source:1.6
target;1.8
compilerverrsion:1.8
理想の挙動:java8でコンパイルされて、java8で動く。
java6だと動かない。


source:1.6
target:1.6
compilerversion:1.8
理想の挙動:java8でコンパイルされて、java6で動く。
java8でももちろん動く。

試してみたら挙動を追記します。

java8でコンパイルすると java.lang.VerifyError

ふっるいソースをjava8でコンパイル+テストしようとしたところ、
以下のようなエラーが出た。

Expecting a stackmap frame at branch target 43
Exception Details:
Location:
jp/sample/xx/xx/.(Lorg/springframework/beans/factory/ListableBeanFactory;)V @39: ifnull
Reason:
Expected stackmap frame at this location.

FuRyu Tech Blog » Blog Archive » 既存プロジェクトをJDK8へ移行したら、java.lang.VerifyErrorが出た
↑参考になりそう。

djunitから脱却したいな~。

sql*plusをlinuxにインストール

最近環境構築ばっかりやってるな。。。

・centos6.5に
・sqlplusを
インストール。

インストールといっても、yumとか使わなくても
zipを展開してパスを通せば使えるようになるらしいので、レッツチャレンジ。

1. 資材調達
instantclient-basic-linux.x64-12.1.0.2.0.zip
instantclient-sqlplus-linux.x64-12.1.0.2.0.zip
を取得。
取得もとは
http://www.oracle.com/technetwork/topics/linuxx86-64soft-092277.html
(ユーザ登録が必要)

rpmも配布してるので、zipがダメだったらrpmでインストールしよう。

2. linux上で展開

/usr/local/bin/instantclient-basic-linux.x64-12.1.0.2.0.zip
という構成にして、
cd /usr/local/bin
unzip instantclient-basic-linux.x64-12.1.0.2.0.zip
unzip instantclient-sqlplus-linux.x64-12.1.0.2.0.zip

3. PATH通す
export PATH=$PATH:/usr/local/bin/instantclient_12_1

4. ためしうち
[root@xxxxx instantclient_12_1]# sqlplus
sqlplus: error while loading shared libraries: libsqlplus.so: cannot open shared object file: No such file or directory

「libsqlplus.so」がないってよ
・・・?ん?
instantclient-sqlplus-linux.x64-12.1.0.2.0.zipの中に入ってるけどなあ。

5. 環境変数に「LD_LIBRARY_PATH=/usr/local/bin/instantclient_12_1」を追加する
PATHだけ通せばいいかと思いきや、「LD_LIBRARY_PATH」という環境変数が必要でした。
sqlplusの内部で必要なのかなあ。

6. リベンジ
[root@xxxxx bin]# sqlplus

SQL*Plus: Release 12.1.0.2.0 Production on Thu Oct 30 13:17:00 2014

Copyright (c) 1982, 2014, Oracle. All rights reserved.

Enter user-name:

リベンジ完了!