nodejsの"nobody" does not have permission to access the dev dir

gypなライブラリをDockerでnpmからインストールする時に-g付けてグローバルに出そうとすると下記のエラーが出て正常にインストールが出来なかった。

gyp WARN EACCES user "nobody" does not have permission to access the dev dir "/usr/lib/node_modules/firebase/node_modules/grpc/.node-gyp/10.5.0"
gyp WARN EACCES attempting to reinstall using temporary dev dir "/usr/lib/node_modules/firebase/node_modules/grpc/.node-gyp"

同じ問題には下記でも出てたみたいで、--unsafe-permにしてやれば良いみたい。

github.com

というわけで以下のような形でDockerfileを作ればOK.

RUN npm --unsafe-perm -g install ...

しかし、面倒ね。

JavaでのUT作成基準を整理してみた

チームが小さいとよしなにですむのだけど、大人数になってくると明文化しとかないと結局テストが書かれないのでUTの作成基準とかを整理してみた。

自分のチームで使う想定のイメージで書いてみたけど、体制やプロダクトの性質によっても変わってくるだろうし色んな人のコメントが聞いてみたいところ。

UT作成基準

前提

  • 本ドキュメントではユニットテストJUnitのような単体テストを実行するテストコードと定義します。
  • また、単体テストとはV字モデルにおけるモジュールの詳細設計に基づきメソッドレベルの振る舞いを保証するテストとします。

ユニットテストとは?

ユニットテストは「動く仕様書」です。

ユニットテストはプロダクトコードの振る舞いを保証してくれます。

何故、ユニットテストを書くのか?

品質と安心のために。ユニットテストを書く事で以下のような効果があります。

不具合発生時の問題の切り分け

結合テスト等のフェーズで問題が発生した際に、UTで書かれてる範囲の内容は問題ないと切り分けできます。

そのためデータとかI/Fの不備とか環境不備とか設計/実装レベルの考慮漏れとかに注力できるので修正コストを下げることが出来ます。

改修時のテスト工数削減

リファクタリングや性能改善をした際にユニットテストに書かれている範囲内で差分が無いことを保証できます。

機能改修の場合も修正箇所のテストを見直すだけになるので、影響調査コストを下げることが出来ます。

リグレッションテスト

リグレッションテストを実施することで「想定外の場所に」影響が出てないことを確認することが出来ます。

人手でやった場合には膨大なコストがかかりますが、UTであればリグレッションテストのコストを大きく下げることが可能です。

いつ、ユニットテストを書くのか?

  • コードレビューのタイミングまでに作成してください
  • TDD/BDD的な観点で書くのが推奨ですが、コードレビュー時までに完成していれば必ずしもテストファーストでなくても問題ありません
  • ユニットテストもプロダクトコードと同様に適切なテストケース(=詳細設計がテストとして記述されているか)になっているかのレビュー対象となります

何をユニットテストに書くのか?

詳細設計にもとづく内容をブラックボックス観点で作成してください。原則、以下の観点が必要です

  • 同値分割
  • 境界値分析
  • 無効値/異常系

また、同値分割が適切であることを確認するために、カバレッジを確認してください。

無効値に関しては本来nullが入りえないモジュールで、入った場合は単純にNullPointerExceptionになるケースなどは作成不要です。特にハンドリングしないシステム異常もUTの作成は不要です。

カバレッジはどうするか?

  • 前述のブラックボックス観点が満たせていれば問題ありませんが、参考値として作成/改修したビジネスロジックの90%から100%をブランチ網羅を目標値としてください。
  • 上記の数字にならない場合はレビュー時にレビューアに理由を説明してください。
  • ブランチ網羅の確認にはJaCoCoを利用してください。

ユニットテストを何に対して書くのか?

  • ユニットテストはプロダクトコードのうちビジネスロジックのpublicメソッドに対して作成します。
  • プレゼンテーションレイヤーや、ビジネスロジックであってもprivateメソッド等には実施しません。
  • ログ出力や標準出力のテストも実施しません。ファイル出力も原則テスト不要です。

Tips:

  • 画面への表示順などはモデルとビューを適切に切り離して、なるべくビジネスロジックのUTで検証できるように設計してください。
  • privateメソッドがテストすべき重要なメソッドである場合はpublicメソッドとして実装し、必要があればクラスを分ける等の対応を行ってください。
  • ログ出力や標準出力、ファイル出力は出力前の内容をテストできるように設計してください。

また、ユニットテストは以下の2つに大別します

  • Quick Test
  • Slow Test

Quick Test

Mavenのビルド時に毎回実行されるテストです。

入出力ファイル、DB、外部API等に依存しないJavaの引数と戻り値のみで期待値検証ができるテストケースを作成してください。

なるべくSlow TestではなくQuick Testで動作が保証できるように設計してください。

Slow Test

通常のMavenビルド時には実行されず「testプロファイル」を指定した際に実行されるテストです。

入出力ファイル、DB、外部API等を利用するテストはこちらに記載します。

SQLが重要なビジネスロジックであるようなケースなど結合テストでの実施ではテストコストが高すぎる場合に作成します。

ファイル出力や外部API出力などQuick Testで十分に内容が保証できる場合は、Slow Testを作成せずに結合テストフェーズでの担保で問題ありません。

いつ、ユニットテストを実行するのか?

  • Quick Testはビルド時に常に回します。原則、skipTests=trueを利用してはいけません。
  • Slow TestはPullRequest前などの節目のタイミングで回します。また、CI環境ではDailyまたは日に数回実行します。

まとめ

とりあえず思いつく事をわーっと、書いてみた。他の人はどうしてるのかも知りたいのでいろんな意見求む!

それではHappy Hacking!

関連リンク

Intel Xeonから見るCPUクロックとコア数の10年間の遷移 - 2017版

タイトルの通りなのですが、良くある「フリーランチは終わった」的な話の資料を作りたくて、CPUクロックとコア数の遷移を追った資料が欲しかったんですがここ最近のものが意外と無かったので作ってみました。

インテル® 製品の仕様情報 - 高度検索 でCPU情報を拾ってGoogle Sheetsで加工した感じ。 Google SheetsにSQLライクな処理が書けるQuery関数はマジ便利ですね。Excelにも導入してほしい...

折角作ったので公開しときます。とりあえずまとめたものは以下。

f:id:pascal256:20171211230859p:plain CPU History - Google スプレッドシート

良い感じにCPUコア数が増えて、クロックが鈍化してるのが分かりますね。 実のところ1998年から2017年までの最高クロックベースで比べると400MHzから4GHzと10倍にはなってるのですが、3GHz超えたあたりからほぼ伸びてないのでグラフ上は変化がない感じになってます。

まあ、分かりきってた話なので可視化しても特に新しい事実は出てこないですが、改めてみると本当にコア数しか増えてないな感はあります。

それではHappy Hacking!

lineageメモ

Cloudera Navigatorみたいにファイルの作成をトレースできるツール Hadoop界隈のものしかない?

テキストマイニングメモ

Webページのメインコンテンツの抽出方法。

「HTMLからのメインコンテンツ抽出 (Main Content Extraction)」とか「本文抽出」とか呼ぶらしい。

Java8を便利にするためのSF(少しFunctional)なライブラリJL2を作ってみた

Java8をもう少しだけ便利に使うための少しFunctionalなライブラリを書いてみました。

github.com

Java8からStream APIが増えて随分コーディングが楽になったんですが、まだまだ不満があります。

その一つが多値を扱うTupleが無いこと。StreamAPIでmapを使って値をこねくり回してると複数の値を返したい時があります。

HaskellScalaならTupleがあります。 RubyやJSなら動的型付けなので、配列に複数の型が入れれます。

では、Javaでは? Mapでは2つの型までしか扱えませんし、ObjectのListや配列もナンセンスです。本来は都度都度適切な型を定義するのがJava流かもですが、いくらなんでも面倒。

というわけで、ScalaのTupleを移植してみました。合わせて、私が良く使う関数型っぽい処理をユーティリライブラリにまとめて公開しました。

使い方

githubリポジトリを作ってるので、Mavenの場合は下記をpom.xmlに追加してもらえれば利用で可能です。

<repositories>
    <repository>
        <id>koduki-repos</id>
        <url>https://raw.githubusercontent.com/koduki/koduki.github.io/mvn-repo/</url>
    </repository>
</repositories>

<dependencies>
    <dependency>
        <groupId>cn.orz.pascal</groupId>
        <artifactId>jl2</artifactId>
        <version>0.1-SNAPSHOT</version>
    </dependency>
</dependencies>

Tuple

全ての基本となるTupleです。まあ、ほぼScalaからの移植です。下記のように使います。

Tuple2<String, Integer> x = new Tuple2<>("A", 10);
Tuple2<Integer, String> y = $(1, "B"); // '$' はシンタックスシュガー
Tuple4<Integer, String, Boolean, List<Integer>> x = $(1, "A", true, Arrays.asList(2)); // Mapと違っていくらでも型が書ける
Tuple2<Integer, Tuple2<String, String>> z = $(1, $("A", "B")); // 入れ子もOK

配列と違って複数の型を管理できます。$関数を一部使ってますが、これはnew TupleXシンタックスシュガーです。

これが使えるだけでStreamAPIをはじめ使い勝手が随分良くなります。

コレクションビルダー

これは特に関数型は関係無いですが、Javaのコレクションは初期化が面倒なのでScalaっぽい記法のシンタックスシュガーを作りました。

Set<String> set = set("a", "b");
List<String> list = list("a", "b");
Map<Integer, String> map = map($(1, "a"), $(2, "b"));

特に、MapはTupleの組み合わせることで通常より格段と書きやすくなってると思います。

Immutable なコレクション操作

たまにJava再帰を書こうとするとImmutable なコレクション操作ができないので直感的に書きづらかったりします。

なので下記のように簡単に書けるライブラリを作りました。

まあ、この辺はより本格的なライブラリがGuavaとかにあると思うので、そっちの方がより便利に使えると思います。

Set<Integer> set1 = set(1, 2);
Set<Integer> set2 = set(3);
Set<Integer> concatedSet = concat(set1, set2);

// create immutable map
ImmutableMap<String, String> map = imap($("k1", "v1"), $("k2", "v2"));
ImmutableMap<String, String> addedMap = map.put("k3", "v3");
ImmutableMap<String, String> removedMap = map.remove("k2");

JDBC Wrapper for StreamAPI

プロトタイプやシンプルなアプリの場合は、ORM使わずに直にJDBC使うこともそれなりにあると思います。

ただ、ResultSetはStreamAPIに対応してないので書くのが少し面倒。というわけで、対応させてみました。

try (
        Connection con = DriverManager.getConnection(JDBC_URL, USER, PASSWORD);
        PreparedStatement pt = con.prepareStatement("select * from persons ");) {
    List<String> ids = QueryStream.of(pt.executeQuery())
            .map(r -> r.getString(1))
            .collect(Collectors.toList());

    assertThat(ids.size(), is(4));
    assertThat(ids.get(0), is("1"));
    assertThat(ids.get(1), is("2"));
    assertThat(ids.get(2), is("3"));
    assertThat(ids.get(3), is("4"));
}

JDBCをもうちょっとモダンに使いたいけど、大げさなライブラリを入れたく無い時に便利です。個人的にはTupleとMapの初期化に次いで良く使う機能です。

まとめ

本格的な関数型サポートを行うライブラリは色々ありますが、そこまで大げさなものは要らないという時に、SFライブラリのJL2はいかがでしょうか?

それでは、Happy Hacking!