JMockitを使ってprivateメソッドをモックにしてみた話

ユニットテスト書いてて、関係ないprivateメソッド通りたくないな〜と思ってググってたらJMockitなるすばらしいものがあったので忘れないうちに!

まず、
Maven Repository: org.jmockit » jmockitからpom.xmljMockitを追加!

<dependency>
    <groupId>org.jmockit</groupId>
    <artifactId>jmockit</artifactId>
    <version>1.30</version>
</dependency>

JmockitSample.class

package com.example;

public class JmockitSample {
	public static void main(String[] args){
		System.out.println(new JmockitSample().methodPublic());
		System.out.println(methodPublicStatic());
		System.out.println(new JmockitSample().callPrivateMethod());
		System.out.println(callPrivateStaticMethod());
	}

	public String methodPublic() {
		return "methodPublic";
	}

	public static String methodPublicStatic() {
		return "methodPublicStatic";
	}

	public String callPrivateMethod(){
		return new JmockitSample().methodPrivate();
	}

	private String methodPrivate() {
		return "methodPrivate";
	}

	public String callPrivateMethodTwoArgs(){
		return new JmockitSample().methodPrivate("TEST", 1);
	}

	private String methodPrivate(String value, Integer no) {
		return value + no;
	}

	public static String callPrivateStaticMethod(){
		return JmockitSample.methodPrivateStatic();
	}
	private static String methodPrivateStatic() {
		return "methodPrivateStatic";
	}

	public void callPrivateVoidMethod(){
		new JmockitSample().voidMethodPrivate(1);
	}

	private void voidMethodPrivate(int no) {}
}

JmockitSampleTest.java

package com.example;

import mockit.Mock;
import mockit.MockUp;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;

import static org.junit.Assert.assertEquals;
import static org.mockito.MockitoAnnotations.initMocks;

public class JmockitSampleTest {

	@InjectMocks
	JmockitSample jmockitSample;

	private int sum = 0;

	@Before
	public void setup() {
		initMocks(this);
	}

	@Test
	public void mockTest(){

		new MockUp<JmockitSample>() {
			@Mock
			private String methodPrivate() {
				return "mocked methodPrivate";
			}

			@Mock
			private String methodPrivate(String value, Integer no) {
				return "mocked methodPrivate two args";
			}

			@Mock
			private String methodPrivateStatic() {
				return "mocked methodPrivateStatic";
			}

			@Mock
			private void voidMethodPrivate(int no) {
				sum += 5;
			}
		};

		assertEquals(new JmockitSample().methodPublic(), "methodPublic");
		assertEquals(JmockitSample.methodPublicStatic(), "methodPublicStatic");
		assertEquals(new JmockitSample().callPrivateMethod(), "mocked methodPrivate");
		assertEquals(new JmockitSample().callPrivateMethodTwoArgs(), "mocked methodPrivate two args");
		assertEquals(JmockitSample.callPrivateStaticMethod(), "mocked methodPrivateStatic");

		new JmockitSample().callPrivateVoidMethod();
		assertEquals(5, sum);
	}
}

privateメソッドのモックができましたー

クライアントとサーバの通信の流れを見てみよう!

入社当時Webの勉強のためにWebサーバ作っていたのですが、まだできておらずWebの理解を深めなきゃという気持ちで再び取り組みはじめました。
今回は、サーバとクライアントの通信がどのように行われているのかについて書きます。

まず、クライアントとサーバを簡単に作ってみましょう。

Client.java

package client;

import java.net.Socket;

public class Client {
    public static void main(String[] args) throws Exception {
        try(Socket socket = new Socket("localhost", 8001)) {
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Server.java

package server;

import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public static void main(String[] args) throws Exception {
        try(ServerSocket serverSocket = new ServerSocket(8001)) {

            System.out.println("クライアントからの接続待ち。");
            Socket socket = serverSocket.accept();
            System.out.println("クライアントからの接続!");

            socket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

サーバを実行した後に、クライアントを実行するとクライアントからサーバへ通信しにいきます。クライアントからサーバで通信するまでに何が行われているのか説明します。流れは、次の通りです。なお、上記のプログラムでは3はやっておりません。

1.ソケットを作る(ソケット作成フェーズ)

まず、サーバがソケットを作成します。ソケットとは、パイプの両端にある出入り口のことです。その時に上記のServer.javaは、8001番ポートで待ち受けています。(「クライアントからの接続を待ちます」と出力)
ソケットができたら、ディスクリプタ(ソケットに割り当てられた番号のようなもの)が返ってきます。アプリケーションはこれを受け取ってメモリに記憶します。ディスクリプタでソケットを識別しています。

2.サーバ側のソケットにパイプをつなぐ(接続フェーズ)

「new Socket("localhost", 8001)」でストリームソケットを作成し、指定されたホスト上の指定されたポート番号に接続します(「クライアント接続」と出力)。繋ぎたいソケットを正確に取得するために、IPとポート番号が必要です。(考え方は手紙の宛名面と同じです。「住所=IPアドレス、宛名=ポート番号」)

3.データを送受信する(送受信フェーズ)

ネットワークを通してデータがサーバに送られます。そして、サーバ側からレスポンスが返ってきます。受信したメッセージを格納するためのメモリ領域を受信バッファと呼びます。

4.パイプを外してソケットを抹消する(切断フェーズ)

繋がっていたパイプを外します。クライアントでもサーバでもどちらから外しても構いません。その後、ソケットを抹消します。

ソケットについてもやもやしていたので少し解消できました!!!

Google Cloud Platformって結構気に入った

Google Cloudで始めるDocker実践入門に参加してました。
講師は、「プログラマのためのDocker教科書」や「Amazon Web Servicesではじめる新米プログラマのためのクラウド超入門」の著者の阿佐志保さんでした。
今回は、もう少し自分で試したいのでDockerの内容は置いといて簡単にGoogle Cloudについて紹介したいと思います。
Google Cloud Platformとは、Googleが提供しているクラウドサービスです。

リージョン

現在利用できるリージョンは、次のとおりです。AWSに比べると全然少ないですね。f:id:b1a9id:20161023220505p:plain

プロダクト 北アメリカ ヨーロッパ アジア
Cloud Storage カウンシルブラフス, バークレー郡, アトランタ サン・ギスラン(ベルギー) 彰化県(台湾)
Cloud Storage(Alpha) レノア, メイズ郡, ダラス - -
Cloud SQL バークレー - アジア
Cloud Datastore バークレー - -

ただ、なんと嬉しいことに2016年中に東京リージョンができるとのこと!その他、オレゴンも追加だそうです。

サービス

全部で48サービス提供されています。(2016年10月23日現在)
主なものをAWSのサービスと比較して紹介してみます。

サービス名 説明 AWSでは
Compute Engine Googleのインフラにホストされている仮想マシンを使った、大規模ワークロードの実行 EC2
App Engine スケーラブルなアプリケーションや、モバイルバックエンド構築のためのプラットフォーム Beanstalk
Cloud Load Balancing Google Cloud Platformのハイパフォーマンス、スケーラブルロードバランサー Elastic Load Balancer
Cloud DNS Googleの世界中のネットワークから供給される高信頼でレジリエントな、低レイテンシーDNS Route 53
Cloud Strage グローバルなエッジキャッシュを備えた、コストバランスに優れるシンプルなオブジェクトストレージ S3
Cloud SQL フルマネージドのMySQLリレーショナルデータベースを利用したデータの保存と管理 RDS
初めて使ってAWSより便利と感じたこと
  • プロジェクトという単位で管理すること

うちの会社ではAWSをつかっておりますが、お客様ごとにアカウントを分けて親アカウントからSwitch rollで目的のアカウントにログインするという運用をしております。毎回これめんどいなーって思ってるんですよね。しかし、GCPではプロジェクトという単位でサービスの管理ができるのです。システムごとにプロジェクトを作成すれば、他のシステムに影響を及ぼす心配もないです。もし、このシステムもういらないってなった場合は、プロジェクトごと削除してしまえばその中で使用していたサービスも一緒に削除できてしまうんです!

  • Cloud Shell

ざっくり言うとブラウザのマネジメントコンソール上にコンソールが起動しちゃうんです。
マネジメントコンソールの左上ヘッダメニュー赤枠のとこを選択すると
f:id:b1a9id:20161023220455p:plain
マネジメントコンソール上にコンソールが起動します。
f:id:b1a9id:20161023220505p:plain
ブラウザからコマンドラインクラウドリソースに直接アクセスできます。クラウドシェルでは、GCPAPIをたたくgcloudコマンドやよく利用すると思われるコマンドがすでにインストールされています。
また、Java、Go、Python、Node.js、PHPRubyの開発・デプロイツールがすでにインストールされているため、Cloud Shellのインスタンス上でアプリを起動してブラウザからプレビューを見ることができます。
1番感動したのは、Cloud Shellを終了してもコマンド履歴やファイルが残っていることです。Cloud Shellでは、Cloud Shellインスタンス上のホームディレクトリに5GBの永続ディスクがマウントされています。永続化されるのは、ホームディレクトリ配下のみみたいです。.bashrcや.vimrcも対象です!


こんな感じでけっこうおもしろそうなのでいろいろ試してみたいと思います。(AWSは12ヶ月の無料枠だが、GCPは60日の無料枠ていうのが残念)

サイ本を読んで~JavaScriptの変数スコープ~

JavaScriptの変数スコープがJavaと結構違って厄介。。
また、備忘録として!
まず、JavaScriptにはブロックレベルのスコープがありません。つまり、ある関数で宣言された変数は、どのブロックで宣言されたにかかわらず関数全体で有効になる。
言葉ではわかりにくいと思うので早速コードで確認!

var scope = "global";
    function f() {
        alert(scope);
        var scope = "local";
        alert(scope);
    }
f();

最初のalertは、「global」と表示されると思いませんか?(JavaとかC使いの人に向けての問いかけ)
上で説明したように関数で宣言した変数は関数全体で有効なので「undefined」と表示されるのです。
もちろん2回目のalertは、「local」と表示されます。
つまり、次のように書いたのと同じ意味になります。

function f() {
    var scope;
    alert(scope);
    var scope = "local";
    alert(scope);
}
f();

変数が後に宣言してあること認識してるなら初期化してくれればいいのにな〜って思った今日この頃。
ま、そうしちゃうとどこで変数宣言したかわからないから探すの大変になっちゃいますけど。。。

言いたいことは、ちゃんと頭で変数宣言したほうがよいよってこと!

サイ本読んで~Part1~

サイ本はJavaScriptの本では一番有名でしょう。ま、どんな本かは調べてもらうとして今回備忘録としてまとめて起きたい項目はちょいちょい記事として書いていこうかと。
Part1は、オブジェクトリテラル
JSではオブジェクトリテラルを利用できる。(あるオブジェクト生成し、そのオブジェクトのプロパティをあらかじめ定義しておくことができる。)
次の形式で定義します。
[プロパティ名:値, プロパティ名:値, ...]
具体的に書くと次のようになります。

var taro = {name: "taro", age:20};
オブジェクトを入れ子にすることも可能。
var myteam = {
    taro: {name: "taro", age: 20},
    hanako: {name: "hanako", age: 18}
};

ThymeleafでformのList<Object>フィールドに値をいれるのにはまった話

仕事しててすごくハマった話。
type="hidden"でListの指定したインデックス番号のidに値をつっこもうと思ったとき、以下のようにするとうまくpostできました。

BookForm.java
@Getter
@Setter
public class BookForm implements Serializable {

	private List<Detail> details;

	@Getter
	@Setter
	public static class Detail implements Serializable {

		private Long id;

		private String name;

		private String passage;
	}
}
index.html
<form class="form-horizontal case-entry" role="form" method="post" th:action="@{/requests/category(step=book-detail)}" th:object="${createForm}">
    <div class="row">
        <div class="col-md-12">
            <fieldset>
                <th:block th:each="selectedBook: ${selectedBooks}">
                    <legend th:text="${selectedBook.name}"></legend>
                        <table>
                            <tr class="col-md-12">
                                <td>
                                     <div class="form-group" th:classappend="${#fields.hasErrors('all')}? has-error">
				         <input type="hidden" th:attr="name='details[' + __${selectedBookStat.index}__ + '].id'" th:value="${selectedBook.id}" />
                                         <div class="col-md-6">
                                             <input type="text" th:field="*{details[__${selectedBookStat.index}__].name}" class="form-control" />
                                         </div>
                                         <div class="col-md-6">
                                             <input type="text" th:field="*{details[__${selectedBookStat.index}__].passage}" class="form-control"  />
                                         </div>
                                     </div>
                                </td>
                            </tr>
                       </table>
                </th:block>
            </fieldset>
        </div>
    </div>
</form>

重要なのはth:attr="name='details[' + __${selectedBookStat.index}__ + '].id'"ですね!!!
th:attrは、任意の属性に値を設定する際に使います!
ちなみに、__は、プリプロセッシングといって通常の式よりも先に評価されます。

ビュー名を返すだけのControllerなら、Controllerは別にいらないらしいよ!

ログインフォームに誘導しようとした際、View名返すだけのControllerを作らなきゃいけません。
そんなとき、以下のようなConfigクラスを作ればこのControllerが必要ないんです!

@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("login").setViewName("login/login-form");
    }
}

ちなみに、Controllerを実装した場合はこんな感じ。

@Controller
@RequestMapping("/login")
public class LoginController {

    @RequestMapping
    public String login() {
        return "login/login-form";
    }
}

これだけ見てみるとそんなにコード量が変わらないというかむしろController作った方が少なそうだけど。笑