最近はScalaとお友達ですが、未だに文法やら動きがワケわかりません。俺、この言語向いてないのかなー、なんて思いつつもチマチマと書いています。
そんな状態なので、Scalaっぽくない書き方をして後で気づいたり、間違えてバグらせたりで、気づいたら「死んでる…」となっていることが多いです。
グチャグチャになる前にテストを書こう、後でリファクタリングするときの役にも立つ、と思い立ちました。
思い立ったまでは良かったものの、Scalaのメジャーなテストフレームワークについて何も知りません。JavaでいうJUnitのようなスタンダードがあるととても助かりますが…。
Scalaのテストを調べてみるとScalaTestというのが有名処のようです。ScalaTestでは「テスト」ではなく「スペック」というようです。
私の理解だと「テストのためにプログラムの動作結果の羅列するのはおかしいんよ。振る舞いや期待する仕様を書けばテストになる方が自然なんよー。」というスタイルみたいです。振る舞いを書くところから開発するスタイルをBDD(Behavior Driven Development、Wikipedia ビヘイビア駆動開発の項)と言うそうです。へぇー。
思想は素晴らしいのですが、ScalaTestは従来のJUnitのような「テスト」から、振る舞いを書く「スペック」まで、何でも出来るようにしてしまったせいで、ゴチャゴチャです。初心者には何を使えば良いか判断がつきません。
とりあえず、良くわからないままですけどFlatSpecで書くことにしました。
私のScalaプログラミング環境はIntelliJ IDEA+Scala Pluginですので、これとScalaTestを連携させたいと思います。
といっても何も難しいことは無く、下記のようなFlatSpecのスペッククラスを書いたらProjectのツリー図から右クリックして、Runを選ぶ、ほぼそれだけです。
import org.scalatest.FlatSpec
class HogeSpec extends FlatSpec with Matchers {
val hoge = "hoge"
val fuga = "fuga"
"hoge" should "starts with 'h'" in {
hoge.startsWith("h") should be (true)
}
}
テスト結果はIntelliJ IDEAの結果表示ウインドウに、ツリー状に見やすく配置されます。IntelliJ IDEA+Scala Plugin+ScalaTestのトリオはなかなか素敵です。
スペックを書く方法はわかりましたが、1度に複数のスペックを動作させるにはどうしたら良いのでしょう?
さらに言うと1度に複数のスペックを動作させた結果が、IntelliJ IDEAの結果表示ウインドウにも出て欲しいですが、どう書いたら良いのでしょう??さっぱりわかりません…。
まとめ用のクラスAllSpecから、先ほどのHogeSpecと新たにFugaSpecを1度に動作させるという想定で、思いつく手だてを試してみたいと思います。
まずは素直に、まとめ用のクラスで各クラスのオブジェクトを作りexecute() を呼びました。
import org.scalatest.FlatSpec
class HogeSpec extends FlatSpec with Matchers {
val hoge = "hoge"
"hoge" should "starts with 'h'" in {
hoge.startsWith("h") should be (true)
}
}
class FugaSpec extends FlatSpec with Matchers {
val fuga = "fuga"
"fuga" should "starts with 'f'" in {
fuga.startsWith("f") should be (true)
}
}
class AllSpec extends FlatSpec {
new HogeSpec().execute()
new FugaSpec().execute()
}
HogeSpec: hoge - should starts with 'h' FugaSpec: fuga - should starts with 'f' AllSpec: AllSpec
コンソールへの出力ではまとまって実行されているように見えますが、入れ子になっていないし、IntelliJ IDEAは「NO TEST」という悲しい結果を表示します。ダメですね。
次にHogeSpecとFugaSpecをシングルトンにしてスペックは関数内に入れ、まとめ用のクラスから呼んでみました。
import org.scalatest.FlatSpec
object HogeSpec extends FlatSpec with Matchers {
def hogeTest {
val hoge = "hoge"
"hoge" should "starts with 'h'" in {
hoge.startsWith("h") should be (true)
}
}
}
object FugaSpec extends FlatSpec with Matchers {
def fugaTest {
val fuga = "fuga"
"fuga" should "starts with 'f'" in {
fuga.startsWith("f") should be (true)
}
}
}
class AllSpec extends FlatSpec {
HogeSpec.hogeTest
FugaSpec.fugaTest
}
AllSpec: AllSpec
何も出なくなりました。ダメだこりゃ…。
最後にHogeSpecとFugaSpecをトレイトにして、まとめ用のクラスにMix-in してみました。
import org.scalatest.FlatSpec
trait HogeSpec extends FlatSpec with Matchers {
val hoge = "hoge"
"hoge" should "starts with 'h'" in {
hoge.startsWith("h") should be (true)
}
}
trait FugaSpec extends FlatSpec with Matchers {
val fuga = "fuga"
"fuga" should "starts with 'f'" in {
fuga.startsWith("f") should be (true)
}
}
class AllSpec extends FlatSpec
with HogeSpec
with FugaSpec {
//empty
}
AllSpec: hoge - should starts with 'h' fuga - should starts with 'f' AllSpec
コンソールへの出力ではまとまって実行されていて、IntelliJ IDEAにもテスト項目が認識されています。これはなかなか良さそうです。しかし、
という欠点もあってイマイチです。でも良い方法がわからんす、困ったもんです。
「大回り乗車」ができる区間であっても、同じ経路を折り返して2度通ると不正乗車になります。下記路線でB駅からD駅に行く切符で、B → A → B → C → Dと乗ることはできません。B → A → Bの分を払わずに乗っているためです。
A - B - C - D
| |
E - F - G
どうしてもB → A → B → C → Dという経路で行きたいなら、どこかでB → A → Bの運賃を追加で払う必要があります。
ええ?そんなのどうやって払うの?と一瞬考えましたが、A駅で一度降りて再び乗れば特に問題ないですね。面倒くさいけど。
折り返したら何が嬉しいか?というと、
という条件のとき、通常の経路B →(普通)→ A → C → Dを使って行くより、B →(普通)→ A →(快速)→ B → C → Dと乗る方が早く着くことがあるので、嬉しいのです。
例えば、つくばに居た時の、研究学園駅(車停めたい時の最寄り)とつくば駅(主要)ですとか、今住んでる摂津富田駅(最寄り)と高槻駅(主要)が当てはまります。
実際には良くても5〜15分早く着くだけでしょうし、そのために追加運賃と乗り換えの手間を掛けることを考えると効果は微妙…。よほど急いでいる時でない限り、積極的にはやりたくないですね。
Facebookに「路線がループしているときの運賃の扱いがよくわからん」的なことを書いたら「大回り乗車」を教えていただいたので、ちょっと調べてみました。
電車の基本ルールとしては、乗る時に使った切符の範囲外に行ってはいけません。例えば、下記路線でB → Cの切符で乗りDまで行った場合、C → Dの運賃を追加で払う必要があります。
A - B - C - D
しかし途中に環状線が入ってたらどうなるでしょう?下記路線でB駅からD駅に行く切符で、B → A → E → F → G → C → Dと乗る場合です。一見するとB駅からG駅までの運賃も払う必要がありそうです。
A - B - C - D
| |
E - F - G
ところがこの場合、ある条件を満たせばB駅からD駅の切符で乗車できます。これがいわゆる「大回り乗車」です。
路線がループしている大都市圏には、運賃計算の特殊ルールが存在します。JRの説明によると「実際に乗った経路に関わらず、最も安い経路で運賃が計算される」というルールです。
このルールを逆手にとって、1駅分の切符を購入し、出発駅→目的の駅→出発駅の隣接駅、と乗ること(大回り乗車)が可能です。条件としては、
です。駅から出ない乗り方、例えば電車や駅の写真を撮りたい人、電車に乗るのが好きな人に向いていますね。駅の外に出たい場合には使えませんけどね。
ちなみに、不正乗車の罰については、鉄道営業法という古い法律(明治三十三年施行)に述べられているようです。罰則を述べていそうなところを摘んでみると、
要は、運賃チョロまかすなんてナメた真似しようもんなら「割増料金を取る」「罰金刑」「電車から叩きだす」ぞ、わかったかこの野郎。ということですね。
個人的には、罰金50円(※)の辺りに時代を感じます。さすが明治施行の法律です。
(※)罰金50円なら超安いしw余裕wwってのは思い違いで、実際には1万円か2万円になるようです(罰金等臨時措置法)。
JRの割増料金も洒落にならないし(鉄道運輸規程 第十九条)、不正乗車はやめた方が良いですねー。
エンジニアを成長させる、たった6つの指針。|クックパッドCTO橋本健太に訊く![前編]
一番わかりやすいコミュニケーションは、「動くもの」と「数字」。エンジニアならば、「これだよ」と作って見せてみる。
(記事中「議論のための議論はしない〜動くものと数字で示す」より引用)
エンジニアなら、ほんとこれしかないと思います。「本番で動くモンはこれ、どう?便利でしょ?」って見せるんです。
ここがダメなら、どんな資料も説明も意味無いでしょう?だってテメーの商品のことはわかった、だが気に入らん、と言われてるに等しいのだから。
メモ: 技術系?の話はFacebookから転記しておくことにした。
Intelは組み込みで勝っているとは言えないけど、なぜだろう。
Atomは電力/性能比はイマイチかもしれないけど結構速い。多くのSoCベンダが採用する省電力プロセスのARM Cortex A9ならいい勝負らしい。
ARM社のデュアルコアCortex-A9、低消費電力版でも性能はAtomを上回る
Atomは以前IP売りしたみたいだけど、売れた様子はない。
元麻布春男の週刊PCホットライン - IntelとARMが目指す、それぞれの隣の芝生
次世代機のPS4, XBox Oneはx86かつPC/ATではない組み込み機器だけど、勝ったのはIntelじゃなくてAMD だった。
PS4もXbox OneもAMDはいってる:2013年末、いよいよ“新生AMD”が新たなステージへ
単純な問題ではないのだろうなあ…。
メモ: 技術系?の話はFacebookから転記しておくことにした。
【イベントレポート】IntelのSDカード型コンピュータ「Edison」の詳細が判明 〜512KBメモリ、2GBストレージ、無線機能などを詰め込む - PC Watch
Quarkが去年発表したモデルから2倍くらい速くなってることと、実際にSDカードサイズのボードを作ってしまう辺りが流石Intel って感じ…。
メモ: 技術系?の話はFacebookから転記しておくことにした。
Gitで相手に変更を取り込んでもらいたいとき、git format-patchで作成したパッチをメールなどで送って、相手にgit amで取り込んでもらう、という方法をよく使っています。
変なコミットメッセージを書いてしまってgit format-patchとgit amで思わぬ罠にハマったので紹介しておきます。
自分から別の人にパッチを送る時の流れを紹介します。まず自分側のリポジトリにて、
パッチを送られた相手は、
とします。
変なコミットメッセージを付けたときのformat-patch, amの動きを見ます。自分のリポジトリはgit_test1にあり、パッチを受け取った相手のリポジトリはgit_test2 にあるものとします。
初めにgit initで自分のリポジトリ(git_test1)を作り、まずは普通のコミットメッセージを付けてコミットします。
$ git --version git version 1.7.10.4
$ mkdir git_test1 $ cd git_test1 $ git init Initialized empty Git repository in /home/katsuhiro/git_test1/.git/ $ touch a $ git add a $ git commit (...略...)
$ touch file1 $ git add file1 $ git commit ---------------------------------------------------------------------- Add: Normal commit Normal commit message. # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # new file: file1 # ---------------------------------------------------------------------- ".git/COMMIT_EDITMSG" [変換済] 12L, 297C書込み [master 9ec38a3] Add: Normal commit 0 files changed create mode 100644 file1
次に変なコミットメッセージを付けてコミットします。
$ touch file2 $ git add file2 $ git commit ---------------------------------------------------------------------- Add: Abnormal commit Abnormal commit message. From 0000000000000000000000000000000000000000 Mon Jun 1 00:00:00 2000 # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # new file: file2 # ---------------------------------------------------------------------- ".git/COMMIT_EDITMSG" [変換済] 14L, 372C書込み [master c4e8588] Add: Abnormal commit 0 files changed create mode 100644 file2
コミットメッセージ中にある、
「From 000000(=SHA1-IDっぽいもの) Mon Jun 1 00:00:00 2000(=日付)」
という行が「変な部分」ですが、コミットの時点では特に何も起きません。
どのコミットのパッチを作れば良いか確かめるため、git logでコミットログを確認します。
$ git log commit c4e85884f643c0e8b107ad0f3e3b0c9fa917409b Author: Katsuhiro Suzuki <katsuhiro@katsuster.net> Date: Sat Jan 11 12:24:17 2014 +0900 Add: Abnormal commit Abnormal commit message. From 0000000000000000000000000000000000000000 Mon Jun 1 00:00:00 2000 commit 9ec38a315e964241f69897728b11f2451eafd700 Author: Katsuhiro Suzuki <katsuhiro@katsuster.net> Date: Sat Jan 11 12:22:49 2014 +0900 Add: Normal commit Normal commit message. commit f3e44926c8f50c557841a157fda89f3fab4aaab3 Author: Katsuhiro Suzuki <katsuhiro@katsuster.net> Date: Sat Jan 11 12:22:21 2014 +0900 Init.
f3e449...(Init), 9ec38a...(Normal), c4e858...(Abnormal) というログになっていますので、Initから後にあるNormalとAbnormal、2つのコミットのパッチを作ります。
$ git format-patch f3e44926c8f50c557841a157fda89f3fab4aaab3 0001-Add-Normal-commit.patch 0002-Add-Abnormal-commit.patch
From 9ec38a315e964241f69897728b11f2451eafd700 Mon Sep 17 00:00:00 2001
From: Katsuhiro Suzuki <katsuhiro@katsuster.net>
Date: Sat, 11 Jan 2014 12:22:49 +0900
Subject: [PATCH 1/2] Add: Normal commit
Normal commit message.
---
0 files changed
create mode 100644 file1
diff --git a/file1 b/file1
new file mode 100644
index 0000000..e69de29
--
1.7.10.4
From c4e85884f643c0e8b107ad0f3e3b0c9fa917409b Mon Sep 17 00:00:00 2001
From: Katsuhiro Suzuki <katsuhiro@katsuster.net>
Date: Sat, 11 Jan 2014 12:24:17 +0900
Subject: [PATCH 2/2] Add: Abnormal commit
Abnormal commit message.
From 0000000000000000000000000000000000000000 Mon Jun 1 00:00:00 2000
---
0 files changed
create mode 100644 file2
diff --git a/file2 b/file2
new file mode 100644
index 0000000..e69de29
--
1.7.10.4
こんなパッチができます。
パッチ適用の前に、パッチを受け取った相手のリポジトリを作ります。
$ mkdir git_test2 $ cd git_test2 $ git init Initialized empty Git repository in /home/katsuhiro/git_test2/.git/
パッチを受け取った相手はgit amでパッチ適用します。
$ mv ../git_test1/000* ./
$ git am 0001-Add-Normal-commit.patch Applying: Add: Normal commit applying to an empty history
順調です。しかし変なコミットのパッチを適用しようとすると、
$ git am 0002-Add-Abnormal-commit.patch Patch is empty. Was it split wrong? If you would prefer to skip this patch, instead run "git am --skip". To restore the original branch and stop patching run "git am --abort".
このように止まってしまいます。
Gitで開発しているオープンソースプロジェクトでは、メーリングリストにgit format-patchのパッチを貼りつけてあることが良くあります。
あるソフトAに最新バグフィクスをバックポートする際に、コミットメッセージを自分で書く代わりに、ソフトAの開発プロジェクトのメーリングリストの文面をコピペしたんですね。
このときformat-patchのヘッダ、つまりFrom 00000... の部分までコミットログに貼り付けてしまったがために、本来はコミットメッセージであるはずの部分をgit amがパッチファイルのヘッダと勘違いして、パッチが変だ、中身が無いよ!と怒っているようです。
「ポケモンバンク」配信を一時停止 アクセス殺到による接続障害緩和のため
クリスマス × ポケモン = 鯖落ち。
ポケモンは恐しい子!
メモ: 技術系?の話はFacebookから転記しておくことにした。
< | 2014 | > | ||||
<< | < | 01 | > | >> | ||
日 | 月 | 火 | 水 | 木 | 金 | 土 |
- | - | - | 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 | - |
合計:
本日: