Javaでprivateメソッドをmockに置き換える方法
探していたら、mockitoでは実装してない旨のページを見つけた。
powermockでできるからそっち使ってねって書いてある(ように読める)…○r2”
社内のMさんとOさんに助けを求めたら教えてもらえたのだが、 どうやら、jmockitなるものを使えば、簡単にstaticだろうがfinalだろうがprivateだろうがmockに置き換えられる模様。 ↓教えてもらったサイト↓ qiita.com
やり方を具体例で教えてもらったが、確かに簡単かつ意図したとおりにできた!
ので、今後お世話になりそうなのでメモしておく。
以下、サンプルコード
package test.jmockit; public class JmockitSample { public static void main(String[] args){ System.out.println(new JmockitSample().methodPublic()); System.out.println(methodPublicStatic()); System.out.println(new JmockitSample().callPraivateMethod()); System.out.println(callPrivateStaticMethod()); } public String methodPublic() { return "methodPublic"; } public static String methodPublicStatic() { return "methodPublicStatic"; } public String callPraivateMethod(){ return new JmockitSample().methodPrivate(); } private String methodPrivate() { return "methodPrivate"; } public static String callPrivateStaticMethod(){ return JmockitSample.methodPrivateStatic(); } private static String methodPrivateStatic() { return "methodPrivateStatic"; } }
実行結果
methodPublic methodPublicStatic methodPrivate methodPrivateStatic
テストコード内で4つのメソッドを前もmockに置き換えて、 Mocked{メソッド名}に変更してみる
package test.jmockit; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; import mockit.Mock; import mockit.MockUp; import org.junit.Test; public class JmockitSampleTest { @Test public void mockTest(){ new MockUp<JmockitSample>() { @Mock public String methodPublic() { return "mocked methodPublic"; } @Mock public String methodPublicStatic() { return "mocked methodPublicStatic"; } @Mock private String methodPrivate() { return "mocked methodPrivate"; } @Mock private String methodPrivateStatic() { return "mocked methodPrivateStatic"; } }; System.out.println(new JmockitSample().methodPublic() ); System.out.println(JmockitSample.methodPublicStatic() ); System.out.println(new JmockitSample().callPraivateMethod()); System.out.println(JmockitSample.callPrivateStaticMethod() ); assertThat(new JmockitSample().methodPublic(), is("mocked methodPublic")); assertThat(JmockitSample.methodPublicStatic(), is("mocked methodPublicStatic")); assertThat(new JmockitSample().callPraivateMethod(), is("mocked methodPrivate")); assertThat(JmockitSample.callPrivateStaticMethod(), is("mocked methodPrivateStatic")); } }
結果
mocked methodPublic mocked methodPublicStatic mocked methodPrivate mocked methodPrivateStatic
注意点
クラスパスがjunitよりも先に来てないとうまく動かないので、その点だけ注意して下さい。 Eclipseだとこんな感じにすればOK
gradleならこんな感じ
apply plugin: 'java' apply plugin: 'eclipse' sourceCompatibility = '1.7' [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' configurations { jmockit_agent } repositories { mavenCentral() } dependencies { jmockit_agent 'com.googlecode.jmockit:jmockit:1.7' testCompile 'com.googlecode.jmockit:jmockit:1.7' testCompile 'junit:junit:4.11' } test { jvmArgs "-javaagent:${configurations.jmockit_agent.asPath}" }
ちなみにNetBeansの場合は、gradle使ってる場合はgradleに完全にコンパイル任せてるのでbulid.gradleの設定さえしておけば、うまく動きます。
参考にしたサイトその1 knjname.hateblo.jp
参考にしたサイトその2 gist.github.com
Base64でデコード出来ない文字をデコードしようとするとどうなるか?
調べても出てこなかったので、メモ。
何かわかりやすいエラーが出るのかとおもいきや、エラーとわかりやすい挙動はしなかった…orz
答えは『実装による』っぽい。 実際に平文→base64.decode→sha256でhash→base64.encodeの処理をOpensslとJavaの両方で試したら挙動に違いがった。
例えばOpenSSLだとこうなる
$ echo "aaaa" | openssl base64 -d | openssl sha -sha256 -binary | openssl base64 # u6nbXovptodruQ0AGBFeI/x0G6a/IyXn/PiO/tdQxMc= $ echo "aaaa,,,," | openssl base64 -d | openssl sha -sha256 -binary | openssl base64 # 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=
aaaa
の場合とaaaa,,,,
の場合で結果が異なる。
,
はbase64で使用していない文字なのでデコード出来ない。
そのため、,
が入るとエラーになって、その結果毎回同じ文字列が返ってきている(ように見える)。
試しにaaaaaaaa,
も試してみる
$ echo "aaaaaaa," | openssl base64 -d | openssl sha -sha256 -binary | openssl base64 # 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=
やっぱり47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=
になる。
なぜ47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=
が返ってくるかはOpenSSLの中身を見ていないのでわからない。
Apache Commons Codecだとこうなる
package Base64Test; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import org.apache.commons.codec.binary.Base64; public class Base64TestExecutor { public static void main(String[] agrs) throws NoSuchAlgorithmException{ String target1 = "aaaa"; String target2 = "aaaa,,,,"; String result1 = testDigest(target1); String result2 = testDigest(target2); System.out.println("result1 : " + result1); System.out.println("result2 : " + result2); System.out.println("isEqual : " + result1.equals(result2)); } private static String testDigest(String targetString) throws NoSuchAlgorithmException { byte[] testResult = Base64.decodeBase64(targetString); MessageDigest md = MessageDigest.getInstance("SHA-256"); byte[] digestVal = md.digest(testResult); String encPassword = Base64.encodeBase64String(digestVal); return encPassword; } }
実行結果
run: result1 : u6nbXovptodruQ0AGBFeI/x0G6a/IyXn/PiO/tdQxMc= result2 : u6nbXovptodruQ0AGBFeI/x0G6a/IyXn/PiO/tdQxMc= isEqual : true ビルド成功(合計時間: 0秒)
こっちは中身を見てみた。
/** * <p> * Decodes all of the provided data, starting at inPos, for inAvail bytes. Should be called at least twice: once * with the data to decode, and once with inAvail set to "-1" to alert decoder that EOF has been reached. The "-1" * call is not necessary when decoding, but it doesn't hurt, either. * </p> * <p> * Ignores all non-base64 characters. This is how chunked (e.g. 76 character) data is handled, since CR and LF are * silently ignored, but has implications for other bytes, too. This method subscribes to the garbage-in, * garbage-out philosophy: it will not check the provided data for validity. * </p> * <p> * Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach. * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ * </p> * * @param in * byte[] array of ascii data to base64 decode. * @param inPos * Position to start reading data from. * @param inAvail * Amount of bytes available from input for encoding. * @param context * the context to be used */ @Override void decode(final byte[] in, int inPos, final int inAvail, final Context context) { if (context.eof) { return; } if (inAvail < 0) { context.eof = true; } for (int i = 0; i < inAvail; i++) { final byte[] buffer = ensureBufferSize(decodeSize, context); final byte b = in[inPos++]; if (b == pad) { // We're done. context.eof = true; break; } else { if (b >= 0 && b < DECODE_TABLE.length) { final int result = DECODE_TABLE[b]; if (result >= 0) { context.modulus = (context.modulus+1) % BYTES_PER_ENCODED_BLOCK; context.ibitWorkArea = (context.ibitWorkArea << BITS_PER_ENCODED_BYTE) + result; if (context.modulus == 0) { buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 16) & MASK_8BITS); buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 8) & MASK_8BITS); buffer[context.pos++] = (byte) (context.ibitWorkArea & MASK_8BITS); } } } } } // Two forms of EOF as far as base64 decoder is concerned: actual // EOF (-1) and first time '=' character is encountered in stream. // This approach makes the '=' padding characters completely optional. if (context.eof && context.modulus != 0) { final byte[] buffer = ensureBufferSize(decodeSize, context); // We have some spare bits remaining // Output all whole multiples of 8 bits and ignore the rest switch (context.modulus) { // case 0 : // impossible, as excluded above case 1 : // 6 bits - ignore entirely // TODO not currently tested; perhaps it is impossible? break; case 2 : // 12 bits = 8 + 4 context.ibitWorkArea = context.ibitWorkArea >> 4; // dump the extra 4 bits buffer[context.pos++] = (byte) ((context.ibitWorkArea) & MASK_8BITS); break; case 3 : // 18 bits = 8 + 8 + 2 context.ibitWorkArea = context.ibitWorkArea >> 2; // dump 2 bits buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 8) & MASK_8BITS); buffer[context.pos++] = (byte) ((context.ibitWorkArea) & MASK_8BITS); break; default: throw new IllegalStateException("Impossible modulus "+context.modulus); } } }
final int result = DECODE_TABLE[b];
の箇所でDECODE出来ない文字の場合は-1
が返される。
そのため、result >= 0
はfalse
になるので、その文字は読み飛ばされる。
その結果、例えばa
とa,,,
の結果はおなじになる。
こういうのは実際に試して動作確認して、中身のソースまで見てみないとわかんないですね!
旧来型IT企業の罪
すごく身に覚えがある。。。 抜け出してよかったなってすごく思う。 http://treeapps.hatenablog.com/entry/2014/04/29/141007
HTML/CSS用テキストエディタ導入メモ~SublimeText~
趣味アプリ作るのに、HTMLでMOCK作ると思います。
その時、Eclipseでも出来るんですけどHTMLとCSSだけ書くのに(あともしかしたらJavascriptも)いいエディタないかなと思って探してみたらSublimeTextというのを見つけました。
カスタマイズしなくても好きなPlugin適当に入れれば使いやすくなりそうなのでちょっと使ってみます。 で、参考にしたサイトをメモのためにペタペタ。
WordPress高速化~オブジェクトキャッシュ編~
友人の会社のサイトが遅いというので、高速化を頼まれて以下を実施したところ劇的に改善した!のでメモっておく。 ※表示に10秒以上かかっていたのが3秒台にまで改善
topコマンドでリソース監視してみたところ、負荷かけたらCPUのwaitがかなり高かったのでCPUがボトルネックになってると判断したのでPHPのオブジェクトキャッシュを導入した。 ※たった2-3リクエストでwaitの値が増えちゃう…。WordPressってすごく重いですね。
PHPは5.3なので、apcというものになるらしい。 ※調べたところそれ以降のバージョンでは標準でopcacheってのがあるらしいのでINIの設定を追加するだけで使える。
参考にしたサイト www.webcreator-net.com
また、Apacheのチューニングもした。
やったのはtop -d1
コマンド叩いてterminalのswapのusedの値見ながら、swapしないぎりぎりの設定を探った。
やっぱり、サイトがさくさく動くと気持ちいいですね。
WordPressの外観のカスタマイズが開けない
という、相談を友人から受けたので調査した。
その結果、Pluginの組み合わせが悪い時にこの現象が起きているようだ。 ソース読んでないので症状からの判断だが、
Page Builder by SiteOrigin && (Dynamic Widgets || WP Widget Cache)
の条件の時に起きるみたい。
※日本語でわかりやすく書くのがめんどくさいからJavaっぽい文法で表現してみた
あと、キャッシュPluginのせいで管理バーがサイトに表示されちゃってたので、
functions.phpに
add_filter( 'show_admin_bar', '__return_false' );
の1行を追加して対応。
WordPress3.1からサイトにも管理バー表示できるようにしたらしいけど、キャッシュ系Pluginと相性悪いみたい。
ってな感じの調査をしてたら疲れたので今日はここまでなり。
さあ、本職のためにJavaの勉強するぞ!!っと。