【ログ】Vuetify追加後にtsconfig.jsonを修正する

※この記事はQrunchログからの移行記事です。

vue add vuetify実行時に追加されるsrc/plugin/vuetify.tsのimport宣言部分は以下のようになっている。

import Vuetify from 'vuetify/lib';

この部分について以下のようなコンパイルエラーが発生する。

モジュール 'vuetify/lib' の宣言ファイルが見つかりませんでした。(中略) Try npm install @types/vuetify if it exists or add a new declaration (.d.ts) file containing declare module 'vuetify/lib';

tsconfig.jsonコンパイラオプションにvuetifyを追加する。

{
  "compilerOptions": {
    ...
    "types": [
-     "webpack-env"
+     "webpack-env",
+     "vuetify"
    ],
    ...
  },
  ...
}

参考

JsRenderの基本的な使い方

6/23追記:Qiitaに転載しました。これからはQiitaを中心に投稿する予定です。 qiita.com 6/23追記ここまで

はじめに

htmlのテンプレートエンジンとしてjQuery Templatesを使用していたが、JSPでは使えなかった。 回避策を紹介しているサイトもあったが、そもそも現在はjQuery Templatesの開発がストップしているということで、後継ライブラリであるJsRenderに切り替えてみた。

導入

公式のサンプルに従ってWebから直接ロード。 jQuery非依存だがjQueryプラグインとしても動作するので今回はjQueryと一緒に使う。

<script src="//code.jquery.com/jquery-1.12.4.js"></script>
<script src="//www.jsviews.com/download/jsrender.js"></script>

使用方法

基礎

html側に<script type="text/x-jsrender>...</script>内に定義したテンプレートとテンプレートを挿入する親要素を記述する。 なお、変数をバインドする部分は{{>変数名}}と記述する。

<ul id="ul1"></ul><!-- 格納先 -->
<script id="tmpl1" type="text/x-jsrender"> <!-- テンプレート -->
  <li>{{>id}} - {{>name}}</li>
</script>

JavaScript側では 1. テンプレートのロードする

var $tmpl1 = $.templates("#tmpl1"); // -> JsRenderのインスタンス生成

または

var $tmpl1 = $("#tmpl1"); // -> jQueryのインスタンス生成
  1. データをバインドしたhtml部品の生成する(上記どちらのインスタンスでも同じ記述になる)
var data1 = {id:"001", name:"hoge"};
var dataHtml = $tmpl1.render(data1); // -> html部品(文字列)の生成
  1. DOMへ追加(以下どちらもjQueryの機能を利用)
$("#ul1").html(dataHtml); // -> #ul1の内容をdataHtmlに差し替え

または

$("#ul1").append(dataHtml); // -> #ul1の末尾にdataHtmlを追加

各関数で何が返却されるのかを確認するために細かく分けたが、再利用する必要がなければ以下のように1行で記述すればよい。

$("#ul1").append($("#tmpl1").render(data1));

⇒表示
f:id:kurukuruz:20180622215629p:plain

基本的な使用例

公式のAPIドキュメントからいくつか抜粋する。
* オブジェクト配列データへのテンプレート適用
「基礎」の項では単独のオブジェクトをテンプレートに適用したが、1データ分のテンプレートに対してオブジェクト配列を投入した場合は自動でイテレーションされる。
また、変数の代わりに#indexを指定すると配列のインデックスが取得できる。

<ul id="ul2"></ul>
<script id="tmpl2" type="text/x-jsrender">
  <li>[{{>#index}}]:{{>id}}/{{>name}}</li>
</script>
var data2 = [
              {id:"001", name:"hoge"},
              {id:"002", name:"foo"},
              {id:"003", name:"bar"},
             ];
$("#ul2").append($("#tmpl2").render(data2));

⇒表示
f:id:kurukuruz:20180622215859p:plain

  • 文字列配列データへのテンプレート適用
    文字列配列の場合は変数として#dataを指定することで取得できる。
<ul id="ul3"></ul>
<script id="tmpl3" type="text/x-jsrender">
  <li>[{{>#index}}] - {{>#data}}</li>
</script>
var data3 = ["one", "two", "three"];
$("#ul3").append($("#tmpl3").render(data3));

⇒表示
f:id:kurukuruz:20180622215926p:plain

  • テンプレート内でのイテレーション
    テンプレート内でイテレーションする場合は
    {{for 配列}}...{{/for}}
    という構文を使用する。
    変数や#index#dataはすべてイテレーション中のオブジェクトでの値になる。
    (以下の例で3行目の{{>id}}と6行目の{{>id}}とでバインドされる値が異なることに注意)
<div id="div4"></div>
<script id="tmpl4" type="text/x-jsrender">
  <div>{{>id}}</div>
  <ol>
    {{for ary1}}
      <li>[{{>#index}}]:{{>id}}/{{>name}}</li>
    {{/for}}
  </ol>
  <ol>
    {{for ary2}}
      <li>[{{>#index}}] - {{>#data}}</li>
    {{/for}}
  </ol>
</script>

⇒表示
f:id:kurukuruz:20180622215946p:plain

  • テンプレート内での条件分岐
    テンプレート内で条件分岐を入れる場合は
    {{if 条件}}...{{else 条件}}...{{else}}...{{/if}}
    という構文を使用する。(「elseif」ではないので注意)
    下記例の通り、条件には簡単な計算や比較演算を入れることも可能。
<ul id="ul5"></ul>
<script id="tmpl5" type="text/x-jsrender">
  <li>
    {{>#data}} :
    {{if #data % 3 == 0}}
      3x
    {{else #data % 3 == 1}}
      3x+1
    {{else}}
      3x+2
    {{/if}}
  </li>
</script>
var data5 = [19, 86, 12, 25];
$("#ul5").append($("#tmpl5").render(data5));

⇒表示
f:id:kurukuruz:20180622220012p:plain

  • htmlタグを適用するバインドとエスケープするバインド
    これまでの例ではすべて変数のバインドに{{>変数}}という記述をしてきたが{{:変数}}という記述も可能である。
    • {{>変数}}: 変数の値をすべて文字列としてバインドする
    • {{:変数}}: 変数の値をhtml部品としてバインドする
<ul id="ul6"></ul>
<script id="tmpl6_1" type="text/x-jsrender">
  <li>{{>id}} - {{>name}}</li>
</script>
<script id="tmpl6_2" type="text/x-jsrender">
  <li>{{:id}} - {{:name}}</li>
</script>
var data6 = {id:"<strong>001</strong>", name:"<del>hoge</del>foo"};
$("#ul6").append($("#tmpl6_1").render(data6));
$("#ul6").append($("#tmpl6_2").render(data6));

⇒表示
f:id:kurukuruz:20180622220030p:plain

参考サイト

JavaとExcelの有効桁数の違いとPOIでの取り込み

概要

JavaExcelとで実数(浮動小数点)の有効桁数が異なる。
Excel内で計算式が適用されているセルをPOIで取り込むとJavaの有効桁数で再計算されるため(?)誤差が生じる。
値によっては人間が見える桁で差が生じてしまうので注意が必要。
※POIのソースを検証したわけではないので原因は推測

検証

Excelでの結果

f:id:kurukuruz:20180524151237j:plain
A1セルはB列の合計値を小数点以下2桁目まで表示したもの、A2セルもB列の合計値。 ここからExcelでは情報落ちにより「厳密に1.115」と判断され、四捨五入の結果「1.12」が表示されていることがわかる。

Javaコード

public static void main(String[] args) throws EncryptedDocumentException, InvalidFormatException, FileNotFoundException, IOException{
    Workbook wb = WorkbookFactory.create(new FileInputStream("data/input2.xlsx"));
    Sheet sheet = wb.getSheet("Sheet1");
    double v = sheet.getRow(0).getCell(0).getNumericCellValue(); // A1セルを取得
    System.out.println(v);

    BigDecimal bd = new BigDecimal(String.valueOf(v));
    System.out.println(bd);
    System.out.println(bd.setScale(2, BigDecimal.ROUND_HALF_UP));
}

Java実行結果

1.1149999999999989
1.1149999999999989
1.11

-1e-15の加算が反映されて「1.11499...」と判断されている。そのため四捨五入の結果は「1.11」になる。

Mockitoのspyと例外(解決編)

概要

spy()で作成したインスタンスに対して、本来例外が発生するパターンでwhen().thenReturn()を用いてモックを定義しようとすると本来の例外が発生してしまいテストができない。 これを回避するためにはdoReturn()を使えばよい。

検証

環境

  • Java8
  • powermock-mockito2-junit-1.7.1
    • junit-4.12
    • mockito 2.8.9
    • powermock 1.7.1

ソースコード

前回と同じTargetクラスを使用

テストコード

package kurukuruz.test.mock;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import org.junit.Test;
import org.mockito.Mockito;

public class TargetTest2 {

    // Mockito.when実行時に例外が発生するパターン
    @Test(expected=IllegalStateException.class)
    public void Mockito_spy_thenReturn() {
        Target t = Mockito.spy(Target.class);
        Mockito.when(t.method1("hoge")).thenReturn("mock");

        t.method1("hoge");
    }

    // 意図通りにモック化するパターン
    @Test
    public void Mockito_spy_doReturn1() {
        Target t = Mockito.spy(Target.class);
        Mockito.doReturn("mock").when(t).method1("hoge");

        String actual = t.method1("hoge");
        assertThat(actual, is("mock"));
    }

    // spy対象外の引数なので実コードを実行し例外発生
    @Test(expected=IllegalStateException.class)
    public void Mockito_spy_doReturn2() {
        Target t = Mockito.spy(Target.class);
        Mockito.doReturn("mock").when(t).method1("hoge");

        t.method1("bar");
    }
}

参考サイト

Mockitoのspyと例外

現象

spy使ってたら例外が出た。
例外のスタックトレースを見ると、when()が実行されるタイミングで実コードが実行されて、発生した例外が持ち出されてくるようだ。
mockを使った場合はこの現象は発生しない。

検証

ソースコード

package kurukuruz.test.mock;

public class Target {

    public String method1(String arg0) {
        throw new IllegalStateException("未実装!");
    }

    public static String smethod(String arg0) {
        throw new IllegalStateException("未実装!");
    }
}

テストコード

package kurukuruz.test.mock;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest({Target.class})
public class TargetTest {

    @Test
    public void Mockito_mock() {
        System.out.println("---Mockito_mock---");
        Target t = Mockito.mock(Target.class);
        Mockito.when(t.method1("hoge")).thenReturn("mock");

        System.out.println(t.method1("hoge"));
        System.out.println(t.method1("foo"));
    }

    @Test
    public void Mockito_spy() {
        System.out.println("---Mockito_spy---");
        Target t = Mockito.spy(Target.class);
        Mockito.when(t.method1("hoge")).thenReturn("mock");

        System.out.println(t.method1("hoge"));
        System.out.println(t.method1("foo"));
    }

    @Test
    public void PowerMockito_mock() throws Exception {
        System.out.println("---PowerMockito_mock---");
        Target t = PowerMockito.mock(Target.class);
        PowerMockito.when(t, "method1", "hoge").thenReturn("mock");

        System.out.println(t.method1("hoge"));
        System.out.println(t.method1("foo"));
    }

    @Test
    public void PowerMockito_spy() throws Exception {
        System.out.println("---PowerMockito_spy---");
        Target t = PowerMockito.spy(new Target());
        PowerMockito.when(t, "method1", "hoge").thenReturn("mock");

        System.out.println(t.method1("hoge"));
        System.out.println(t.method1("foo"));
    }

    @Test
    public void PowerMockito_mockStatic() {
        System.out.println("---PowerMockito_mockStatic---");
        PowerMockito.mockStatic(Target.class);
        Mockito.when(Target.smethod("hoge")).thenReturn("mock");

        System.out.println(Target.smethod("hoge"));
        System.out.println(Target.smethod("foo"));
    }
}

実行結果

---PowerMockito_mock---
mock
null
---PowerMockito_mockStatic---
mock
null
---Mockito_spy---
---Mockito_mock---
mock
null
---PowerMockito_spy---

↓エラーが発生したケースのスタックトレース
f:id:kurukuruz:20180521114755j:plain

Mockito+PowerMockで不可視メソッドをspy

目的

privateメソッドやパッケージ違いの親クラスのprotectedメソッドのようにテストコードから不可視なメソッドにspyを適用したい。

方法

PowerMockito.whenを使う。

Target target = PowerMockito.spy(new Target());
PowerMockito.when(target, "invisibleMethod", "hoge").thenReturn("mock-string");


(参考:Mockitoのspy)

Target target = Mockito.spy(new Target());
Mockito.when(target.method("hoge")).thenReturn("mock-string");

検証

いろんな不可視パターンで試してみる

ソースコード

テスト対象クラス
package kurukuruz.test.mock;

import kurukuruz.test.mock.base.Parent;

public class Target extends Parent {

    public String executeOwnMethod(){
        return "own method return: " + private1();
    }

    private String private1() {
        return "own";
    }

    public String executeParent1() {
        return "method1 return: " + method1();
    }

    public String executeParent2() {
        return "method2 return: " + method2();
    }

    public String executeParent3() {
        return "method3 return: " + method3();
    }

    public String executeParent4(String hoge) {
        return "method4 return: " + method4(hoge);
    }

    public String executeParent5(Dao dao) throws Exception {
        return "method5 return: " + method5(dao);
    }

}
親クラス
package kurukuruz.test.mock.base;

import lombok.Getter;
import lombok.Setter;

public class Parent {

    /** publicメソッド */
    public String method1() {
        return "method1";
    }

    /** protectedメソッド */
    protected String method2() {
        return "method2";
    }

    /** protected finalメソッド */
    protected final String method3() {
        return "method3";
    }

    /** 引数あり(文字列) */
    protected final String method4(String hoge) {
        return hoge;
    }

    /** 引数が任意のオブジェクト */
    protected final String method5(Dao dao) throws Exception {
        if(dao.getName() == null) {
            throw new Exception("name is null!");
        }
        return dao.getName();
    }

    @Setter
    @Getter
    public static class Dao {
        private String name;
    }
}

テストコード

package kurukuruz.test.mock;

import kurukuruz.test.mock.base.Parent.Dao;

import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.experimental.runners.Enclosed;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(Enclosed.class)
public class TargetTest {

    public static class そのまま実行 {

        @BeforeClass
        public static void setupBeforeClass() {
            System.out.println("---そのまま実行---");
        }

        @Test
        public void テスト0() throws Exception{
            Target t = new Target();
            System.out.println(t.executeOwnMethod());
        }

        @Test
        public void テスト1() throws Exception{
            Target t = new Target();
            System.out.println(t.executeParent1());
        }

        @Test
        public void テスト2() throws Exception{
            Target t = new Target();
            System.out.println(t.executeParent2());
        }

        @Test
        public void テスト3() throws Exception{
            Target t = new Target();
            System.out.println(t.executeParent3());
        }

        @Test
        public void テスト4() throws Exception{
            Target t = new Target();
            System.out.println(t.executeParent4("hoge"));
        }

        @Test
        public void テスト5() throws Exception{
            Target t = new Target();
            Dao param1 = new Dao();
            param1.setName("foo");
            System.out.println(t.executeParent5(param1));
        }
    }

    @RunWith(PowerMockRunner.class)
    @PrepareForTest({Target.class})
    public static class モック実行 {

        @BeforeClass
        public static void setupBeforeClass() {
            System.out.println("---モック実行---");
        }

        @Test
        public void テスト0() throws Exception{
            Target t2 = PowerMockito.spy(new Target());
            PowerMockito.when(t2, "private1").thenReturn("mock0");
            System.out.println(t2.executeOwnMethod());
        }

        @Test
        public void テスト1() throws Exception {
            Target t2 = PowerMockito.spy(new Target());
            PowerMockito.when(t2, "method1").thenReturn("mock1");
            System.out.println(t2.executeParent1());
        }

        @Test
        public void テスト2() throws Exception {
            Target t2 = PowerMockito.spy(new Target());
            PowerMockito.when(t2, "method2").thenReturn("mock2");
            System.out.println(t2.executeParent2());
        }

        @Test
        public void テスト3() throws Exception {
            Target t2 = PowerMockito.spy(new Target());
            PowerMockito.when(t2, "method3").thenReturn("mock3");
            System.out.println(t2.executeParent3());
        }

        @Test
        public void テスト4() throws Exception {
            Target t2 = PowerMockito.spy(new Target());
            PowerMockito.when(t2, "method4", "hoge").thenReturn("mock4");
            System.out.println(t2.executeParent4("hoge"));
            System.out.println(t2.executeParent4("foo"));
        }

        @Test
        public void テスト5() throws Exception {
            Target t2 = PowerMockito.spy(new Target());
            Dao dao = new Dao();
            dao.setName("bar");
            PowerMockito.when(t2, "method5", dao).thenReturn("mock5");
            System.out.println(t2.executeParent5(dao));
        }
    }
}

実行結果

---そのまま実行---
own method return own
method1 return method1
method2 return method2
method3 return method3
method4 return hoge
method5 return foo
---モック実行---
method4 return mock4
method4 return foo
method1 return mock1
own method return mock0
method2 return mock2
method3 return mock3
method5 return mock5

参考サイト

Servletクラスのテスト

目的

JSONを返却するServletクラスの単体テストがしたい。

概要

  • GETメソッドのサーブレット
    • doGetをたたく
    • 引数にはMockHttpServletRequest, MockHttpServletResponseを利用
  • Connectionはデータソースを利用
    • PowerMockでモック
      • JUnitのTheoryも併用

検証プログラム

概要

  • idを渡して該当するレコードの内容をJSON形式で返却する
  • 該当idがない場合は空オブジェクトを返却する
  • DBデータ(テーブル:voiceact)
uid uname age office
aimi 愛美 26
goto 後藤沙緒里 31 null

ソース

サーブレット

package kurukuruz.sample.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import kurukuruz.sample.servlet.connection.ConnectionPool;
import kurukuruz.sample.servlet.util.User;
import net.arnx.jsonic.JSON;

/**
 * Servlet implementation class Search
 */
@WebServlet(name = "search", urlPatterns = { "/search" })
public class Search extends HttpServlet {
    private static final long serialVersionUID = 1L;

    /**
    * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
    */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // パラメータ取得
        String id = request.getParameter("id");

        // 検索実行
        User user = searchById(id);

        // JSON作成
        String result = "{}";
        if(user != null){
            result = JSON.encode(user);
        }

        // 返却
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        PrintWriter pw = response.getWriter();
        pw.print(result);
    }

    private User searchById(String id){
        User user = null;

        Connection conn = null;
        PreparedStatement ps = null;
        try {
            conn = ConnectionPool.getConnection();

            String sql =
                    "SELECT uname, age, office "
                    + "FROM voiceact "
                    + "WHERE uid = ?";

            ps = conn.prepareStatement(sql);
            ps.setString(1, id);

            ResultSet rs = ps.executeQuery();
            if(rs.next()){
                user = new User(rs.getString(1), rs.getInt(2), rs.getString(3));
            }

        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            if(ps != null){
                try {
                    ps.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(conn != null){
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }


        return user;
    }

}

テストクラス

package kurukuruz.sample.servlet;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.Map;

import javax.servlet.ServletException;

import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Setter;
import kurukuruz.sample.servlet.connection.ConnectionPool;
import kurukuruz.sample.servlet.testbase.ServletTestBase;
import net.arnx.jsonic.JSON;

import org.junit.Ignore;
import org.junit.Test;
import org.junit.experimental.theories.DataPoints;
import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.modules.junit4.PowerMockRunnerDelegate;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;

@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(Theories.class)
@PrepareForTest({ConnectionPool.class})
public class SearchTest extends ServletTestBase {

    @DataPoints
    public static final Fixture[] fixtures = {
        new Fixture("aimi", "愛美", new BigDecimal(26), "響", false),
        new Fixture("goto", "後藤沙緒里", new BigDecimal(31), null, false),
        new Fixture("ayasa", null, null, null, true),
        new Fixture("", null, null, null, true),
        new Fixture(null, null, null, null, true),
    };

    @Theory
    @Test
    public void test(Fixture fixture) throws ServletException, IOException {
        MockHttpServletRequest req = new MockHttpServletRequest();
        MockHttpServletResponse res = new MockHttpServletResponse();

        req.setParameter("id", fixture.id);

        Search target = new Search();
        target.doGet(req, res);

        @SuppressWarnings("rawtypes")
        Map result = JSON.decode(res.getContentAsString());

        if(fixture.isEmpty){
            assertTrue("Mapの空判定", result.isEmpty());
        }else{
            assertThat("name判定", result.get("name"), is(fixture.name));
            assertThat("age判定", result.get("age"), is(fixture.age));
            assertThat("pc判定", result.get("pc"), is(fixture.pc));
        }
    }

    @Ignore
    @NoArgsConstructor
    @AllArgsConstructor
    @Setter
    public static class Fixture {
        // 検索条件
        private String id;
        // 想定結果
        private String name;
        private BigDecimal age;
        private String pc;
        private boolean isEmpty;
    }

}

テストベースクラス

package kurukuruz.sample.servlet.testbase;

import static org.mockito.Mockito.*;

import java.sql.Connection;
import java.sql.DriverManager;

import kurukuruz.sample.servlet.connection.ConnectionPool;

import org.junit.Before;
import org.junit.BeforeClass;
import org.powermock.api.mockito.PowerMockito;

public class ServletTestBase {

    protected static Connection Conn;

    @BeforeClass
    public static void setupBeforeClass() throws Exception {
        Class.forName("org.postgresql.Driver");
    }

    @Before
    public void setup() throws Exception {
        Conn = DriverManager.getConnection("jdbc:postgresql://localhost:5432/test", "user", "pass");

        PowerMockito.mockStatic(ConnectionPool.class);
        when(ConnectionPool.getConnection()).thenReturn(Conn);
    }

}

参考サイト