JUnit 実践入門 体系的に学ぶユニットテストの技法 第15章 継続的 テスト すばやいフィードバックを手に入れる
第15章 継続的テスト すばやいフィードバックを手に入れる
- 開発においては、欠陥の修正による恩恵と、リスクのバランスを示している考慮する必要がある。
- テストを早い段階から繰り返し行うことで、リスクをカバーできる。
15.1 継続的テスト
ユニットテストを開発に導入するためには、効果を体感させることが重要であり、その方法として、継続的テストが有用である。
開発チームへのすばやいフィードバック
ユニットテストの恩恵は、「開発チームへのすばやいフィードバック」である。
早期からテストする
自動的にテストする
ユニットテストは、実行を自動化できる。
繰り返しテストする
リグレッションの早期検知、修正が可能となるため、リグレッションが発生することを恐れ、コード修正が行えなくなる事態を回避できる。
継続的テストとは
- 継続的テスト continuous testing とは、早い段階からテストを繰り返し実施するプラクティスのこと。
- ソフトウェアの品質を直接的には高めないが、開発チームに、ソフトウェアリリースへの自信を与える。
継続的テストを行うための3つの要素
以下を現代ソフトウェア開発の三本柱という。
- ユニットテスト
- 自動化
- バージョン管理
ユニットテスト
JUnitを利用してテストコードを書くことで、プログラムとして実行可能となる。
自動化
バージョン管理
- テスト失敗時に差分を確認することで、原因を究明できる。
- 原因を救命できない際に、ひとまず一つ前の正常バージョンに切り戻すことができる。
継続的テストの運用
- バージョン管理システムに変更があったタイミングで、ユニットテストを含む自動化されたビルドプロセスが実行されるのが一般的。
- テスト失敗をゼロにすることが大事なのではなく、発生した失敗を早期に検知・修正することが大事。
継続的テストとリファクタリング
継続的テストがリファクタリングを行うことを心理面で容易にし、結果としてきれいなソースコードをもたらす。
ユニットテストとリファクタリング
- ソースコードのメメンテナンス性工場等の目的で実施する。
- 特に重複を減らし、再利用や拡張が行い易い設計にする目的で行われることが多い。
- ユニットテストの実施が担保されてはじめて、リファクタリングができる。
積極的なリファクタリング
継続的テストが実施されている環境では、開発者は積極的にリファクタリングを行うことができる。
15.2 Maven によるビルドプロセスの自動化
Maven とは
Maven の準備
Eclipse から 管理することも可能であるが、基本的にはコマンドラインから実行するツールであるため、公式サイトからダウンロードしたアーカイブを適当なディレクトリに展開し、パスを通しておく。
Maven プロジェクトと POM
- Maven も Eclipse 同様、ソースコードやリソースをプロジェクト単位で管理する。
- Maven プロジェクトでは、ルートディレクトリに XML ファイルを配置し、プロジェクトの構成を定義する。
- この定義ファイルのことは POM project object model と呼ばれ、通常 pom.xml というファイル名とする。
- プロジェクト構成を定義した POM を追加すれば、どんなプロジェクトでも Maven を利用できる。
- ただし Maven プロジェクトの標準的にディレクトリ構成に合わせておくのがベター。
Maven プロジェクトの作成
- Eclipse から簡単に作成できる。
Maven のプロジェクト構成
- 標準では、プロダクションコードとテストコードを独立したソースフォルダに作成する。リソースファイルも独立したソースファイルに作成するため、4つのソースフォルダが作成される。
- ビルド時の成果物は target 配下に出力されるため、ソースコード管理システム(Subversion, Git 等)利用時は、target フォルダを除外フォルダとすること。
依存ライブラリの管理
依存ライブラリを追加するには、そのライブラリのグループId、アーティファクトId、バージョンを POM に設定すれば良い。
Maven のリポジトリ
- リポジトリとは、Maven でライブラリ(Maven プロジェクトの成果物)を管理するデータベースのこと。
- JUnit のようなメジャーなライブラリは、セントラルリポジトリと呼ばれる標準リポジトリで見つけられる。
- Maven では、一度ダウンロードされたライブラリはローカルマシンのリポジトリ、ローカルリポジトリに保存している。
依存ライブラリの追加
ソースコードのエンコーディング設定
- Windows, MacのデフォルトエンコーディングMS932(Shift-JIS)ではなく、Java では UTF-8 が無難。
- Maven プロジェクトのソースコードエンコーディングは、POM の properties セクションに project.build.sourceEncoding タグを追加して指定する。
Maven のプラグイン
プラグインは、大きく4つに大分される。 - コンパイルやテストといったコア機能に関連するもの - JAR や WAR の作成などパッケージングに関連するもの - テスト結果や Javadoc などのレポート作成に関連するもの - それ以外の拡張機能に関連するもの
プラグインの設定
Maven によるビルドの実行とフェーズプラグインの設定
テストをスキップする
Eclipse から実行する
プラグインとゴール
ゴールを実行する
Maven によるカテゴリ化リスト
Maven によるカバーレッジレポート
JaCoCo プラグインの導入
JaCoCo によるカバーレッジレポート
15.3 バージョン管理システムによる継続的テストの運用
バージョン管理システムとは
バージョン管理システムへのコミット
Subversion でのテストサイクル
Git や Mercurial でのテストサイクル
15.4 Jenkins による継続的インテグレーション
継続的インテグレーションとは
Jenkins とは
Jenkins の導入
Jenkins のジョブ
ジョブの作成
ジョブの設定
バージョン管理システムの設定
ビルドゴールのの設定
ジョブの実行
ビルドトリガーの設定
Jenkinsのリモートビルド機能
バージョン管理システムのフック機能
継続的インテグレーションの効果
テストの実行コスト低減
すばやいフィードバック
テスト結果の履歴
JUnit 実践入門 体系的に学ぶユニットテストの技法 第16章 テスト駆動開発 テストファーストで設計する
第16章 テスト駆動開発 テストファーストで設計する
16.1 テスト駆動開発 Test Driven Development とは
- TDD は、ユニットテストをプロダクションコードよりも先に記述することを原則とした開発手法のこと。
- この原則はテストファーストと呼ばれ、XPのプラクティスの一つ。
- TDD は、ゴールを少し先に設定し、それを短期間でクリアすることを繰り返し、最終的なゴールを目指す。
- 一気に飛躍した結果を求めない。
16.2 テスト駆動開発のサイクル
- 設計する
- テストコードを書く
- レッド: テスト失敗
- プロダクションコードを書く
- グリーン: テスト成功
- リファクタリングする
- グリーン: テスト成功
このサイクルを、レッド - グリーン - リファクタリングと呼ぶ。
設計する
- 何をテストしたらよいかわかるまで、設計する。
- 主にインターフェイスの観点から設計を行う。
- 仔細に至る部分は、作ってみなければわからない事が多く、はじめから完璧に洗い出すことは難しいため。
テストコードを書く
- 動作するドキュメントとしてのテストコードを書く。ただし、1サイクルで作成するテストケースは、原則一つ。
プロダクションコードを書く
- コンパイルエラーの除去
- テストコード実行 -> レッド(エラー) (未実装のため)
- グリーンになるよう、手を加える
- 脇目をふらず、ただグリーンにすることを目的に!
- 例外処理等はここでは実装せず、次のサイクルでユニットテストを作成した後に実装する
リファクタリングを行う
- テストがグリーンを維持するよう、リファクタリングを実施。
- TDD ではリファクタリングが重要。
- ユニットテストケースがあるため、修正が挙動に影響を及ぼすかすぐに確認できる。
- 安心してリファクタリングに望める!
次のサイクルを始める
- ここまでのサイクルをリズムよく、早く回すことが、TDD では重要。
- 1サイクルが短いほど、ミスした時の手戻りが少ないから。
- 目安としては 10 から 15 分を目安に。
- どれだけサイクルを回せばよいかについては、コストとの相談。
16.3 Calculator クラスのテスト駆動開発
実装しつつ、前述の事項を確認する。
テストファースト
コンパイルエラーの解決
仮実装
リファクタリング
三角測量
16.4 テスト駆動開発の目的
ステップバイステップ
- テスト駆動開発の基本は、「小さく」「1つずつ」
- 自分を過信してたくさんのことを一気にやろうとするべからず
自分が最初のユーザ
- テスト対象となるクラス、メソッドを使って自分が最初にテストコードを書く
- 使いにくいことに、早い段階で気づけるチャンスが有る
動作するきれいなコード
- TDD では、はじめにテストケースを作成し、動作するプロダクションコードはなにはともあれ、まず作成する。
- はじめはきれいなコードではないが、リファクタリングにより、美しいコードとすることを目指す。
- 完璧な設計をはじめから行うことはできないか、あるいは非常にコストがかかる、との知見から来ている。
素早いフィードバック
- TDD と CI をプロダクション組み合わせることで、素早いフィードバックが保証される。
- 『プログラマの安心』の源泉。
メンテナンスされたドキュメント
味噌ラーメン 玉ねぎとひき肉炒めたの載せ
24時過ぎの晩飯。
問うまでもなく、ギルティである。
Eclipse が起動しなくなったので eclipse.ini の pleiades.jar のパスを、絶対指定にした
Eclipseが起動しなくなった。
スプラッシュウィンドウすら出ない。
Pleiades の日本語化適用しているので、eclipse.ini から日本語化適用設定を外してみる。
おなじみのケプラーさんのスプラッシュウィンドウが表示された。
ということで、原因はこいつ。
相対パスでなく、絶対パスで指定することにした。
-javaagent:C:\pleiades\eclipse\features\dropins\MergeDoc\eclipse\plugins\jp.sourceforge.mergedoc.pleiades\pleiades.jar
問題なく起動。
明確な原因は不明であるが、直前にJDKインストールしたから、そのせいかも・・・
JUnit 実践入門 体系的に学ぶユニットテストの技法 付録A 開発環境のセットアップ
付録A 開発環境のセットアップ
A.1 JDK のセットアップ
- pleiades 同梱の JDK1.7 を流用
A.2 Eclipse のセットアップ
- もろもろ面倒なので、アルゴリズム勉強用とは別途用意することにした
- 日本語化せず(pleiades でなく)英語版でいく
- 4.3 - Kepler にした
JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)
- 作者: 渡辺修司
- 出版社/メーカー: 技術評論社
- 発売日: 2012/11/21
- メディア: 単行本(ソフトカバー)
- 購入: 14人 クリック: 273回
- この商品を含むブログ (65件) を見る
明解 Javaによるアルゴリズムとデータ構造 6-4_単純挿入ソート
6-4 単純選択ソート straight insertion sort
『原列の先頭要素を、目的列内の適切な位置に挿入』を、n - 1 回繰り返す
- トランプのカード並べに似たソート(7並べをイメージしてね)
実装としては、
- 『左隣の要素が、現在着目している要素より大きい限り、その値を代入』。 tmp に a[i] を保持しつつ、繰り返し用変数 j に i - 1 を代入しておき、以下のいずれか一方を満たせば終了。
- 目的列の左端に達する
- tmpと等しいか小さいキーを持つ a[j] に出会う
- ド・モルガンの法則から、以下2つの条件が両方成立しているあいだ繰り返す
- j が 0 より大きい
- a[j - 1] の値が tmp より大きい
- 操作が完了すると、a[j] に tmp を単純挿入する
- 『左隣の要素が、現在着目している要素より大きい限り、その値を代入』。 tmp に a[i] を保持しつつ、繰り返し用変数 j に i - 1 を代入しておき、以下のいずれか一方を満たせば終了。
飛び越えた要素の交換は実施しないので、安定なアルゴリズム
- 比較回数: 注目要素を目的列に挿入する際の、平均比較回数
疑問
本だと 、時間計算量
なんだけど、どういう計算なんだろう。
ソースコードと実行結果
import java.util.Scanner; class InsertionSort { static void insertionSort(int[] a, int n) { int ccnt = 0; // 比較回数 int scnt = 0; // 挿入回数 for (int i = 1; i < n; i++) { System.out.println("パス: " + i); for (int e : a) { System.out.printf("%3d%c", e, (e == a[i]) ? '*' : ' '); // '*' -> ソートする要素 } System.out.println(); int j; int tmp = a[i]; for (j = i; j > 0 && a[j - 1] > tmp; j--) { a[j] = a[j - 1]; scnt++; for (int e : a) { System.out.printf("%3d%c", e, (e == a[j - 1]) ? '+' // '+' -> コピーされる要素 : (e == a[j]) ? '-' : ' '); // '-' -> 上書かれる要素 } System.out.println(); ccnt++; } a[j] = tmp; for (int e : a) { System.out.printf("%3d%c", e, ' '); } System.out.println(); System.out.println("比較回数: " + ccnt); System.out.println("交換回数: " + scnt); } } public static void main(String[] args) { Scanner stdIn = new Scanner(System.in); System.out.println("単純挿入ソート"); System.out.print("要素数: "); int nx = stdIn.nextInt(); int[] x = new int[nx]; for (int i = 0; i < nx; i++) { System.out.print("x[" + i + "]: "); x[i] = stdIn.nextInt(); } insertionSort(x, nx); System.out.println("昇順にソートしました"); for (int i = 0; i < nx; i++) { System.out.println("x[" + i + "]=" + x[i]); } } }
単純挿入ソート
要素数: 4
x[0]: 4
x[1]: 3
x[2]: 2
x[3]: 1
パス: 1
4 3* 2 1
4+ 4+ 2 1
3 4 2 1
比較回数: 1
交換回数: 1
パス: 2
3 4 2* 1
3 4+ 4+ 1
3+ 3+ 4 1
2 3 4 1
比較回数: 3
交換回数: 3
パス: 3
2 3 4 1*
2 3 4+ 4+
2 3+ 3+ 4
2+ 2+ 3 4
1 2 3 4
比較回数: 6
交換回数: 6
昇順にソートしました
x[0]=1
x[1]=2
x[2]=3
x[3]=4
- 番兵法 sentinel を適用して、終了条件をひとつにした版のメソッド
class InsertionSortSen { static void insertionSort(int[] a, int n) { int ccnt = 0; // 比較回数 int scnt = 0; // 挿入回数 for (int i = 2; i < n; i++) { System.out.println("パス: " + i); for (int e : a) { System.out.printf("%3d%c", e, (e == a[i]) ? '*' : ' '); // '*' -> ソートする要素 } System.out.println(); int j; int tmp = a[i] = a[0]; for (j = i; a[j - 1] > tmp; j--) { a[j] = a[j - 1]; scnt++; for (int e : a) { System.out.printf("%3d%c", e, (e == a[j - 1]) ? '+' // '+' -> コピーされる要素 : (e == a[j]) ? '-' : ' '); // '-' -> 上書かれる要素 } System.out.println(); ccnt++; } if (j > 0) { a[j] = tmp; } for (int e : a) { System.out.printf("%3d%c", e, ' '); } System.out.println(); System.out.println("比較回数: " + ccnt); System.out.println("交換回数: " + scnt); } } }
2分挿入ソート binary insertion sort
- 配列が大きくなるにつれ、要素の挿入に要する比較・代入のコストも大きくなる。挿入すべき位置の探索に、二分探索を使うことでコスト削減を実現している。
- 代入に関しては軽くなってない。
ソースコード
import java.util.Scanner; class BinaryInsertionSort { static void insertionSort(int[] a, int n) { for (int i = 1; i < n; i++) { System.out.println("パス: " + i); int j = binarySearch(a, a[i], i - 1); int tmp = a[i]; for (int k = 0; k < n; k++) { System.out.printf("%3d%c", a[k], (k < i) ? '-' // '-' -> 目的列 : (k == i) ? '*' // '*' -> ソート対象 : ' '); // ' ' -> 原列 } System.out.println(); for (int k = i; k > j; k--) { a[k] = a[k - 1]; } a[j] = tmp; for (int k = 0; k < n; k++) { System.out.printf("%3d%c", a[k], (k < j) ? '-' // '-' -> 目的列 : (k == j) ? '*' // '-' -> ソート対象の挿入先 : (k < i + 1) ? '-' // '-' -> 目的列 : ' '); // ' ' -> 原列 } System.out.println(); } } /** * ある要素が、既存の昇順ソート済int配列のどのインデックスに挿入されるべきか、あるべきインデックスを返すメソッド * * @param a * @param key * @param tailIdx * @return 挿入すべきインデックス */ static int binarySearch(int[] a, int key, int tailIdx) { int headIdx = 0; int centerIdx = -1; int index = -1; while (headIdx <= tailIdx) { centerIdx = (headIdx + tailIdx) / 2; if (a[centerIdx] == key) { index = centerIdx + 1; // 既に同値が存在すれば、次のインデックスを返す break; } else if (key < a[centerIdx]) { // keyが前方に存在 tailIdx = centerIdx - 1; index = centerIdx; // 現在のcenterIdxを代入 } else { // keyが後方に存在 headIdx = centerIdx + 1; index = headIdx; // 次のheadIdxを代入 } } return index; } public static void main(String[] args) { Scanner stdIn = new Scanner(System.in); System.out.println("2分挿入ソート"); System.out.print("要素数: "); int nx = stdIn.nextInt(); int[] x = new int[nx]; for (int i = 0; i < nx; i++) { System.out.print("x[" + i + "]: "); x[i] = stdIn.nextInt(); } insertionSort(x, nx); System.out.println("昇順にソートしました"); System.out.println("-------------"); for (int i = 0; i < nx; i++) { System.out.println("x[" + i + "]=" + x[i]); } } }
- 実行結果
2分挿入ソート
要素数: 10
x[0]: 9
x[1]: 1
x[2]: 4
x[3]: 5
x[4]: 3
x[5]: 8
x[6]: 4
x[7]: 58
x[8]: 7
x[9]: 6
パス: 1
9- 1* 4 5 3 8 4 58 7 6
1* 9- 4 5 3 8 4 58 7 6
パス: 2
1- 9- 4* 5 3 8 4 58 7 6
1- 4* 9- 5 3 8 4 58 7 6
パス: 3
1- 4- 9- 5* 3 8 4 58 7 6
1- 4- 5* 9- 3 8 4 58 7 6
パス: 4
1- 4- 5- 9- 3* 8 4 58 7 6
1- 3* 4- 5- 9- 8 4 58 7 6
パス: 5
1- 3- 4- 5- 9- 8* 4 58 7 6
1- 3- 4- 5- 8* 9- 4 58 7 6
パス: 6
1- 3- 4- 5- 8- 9- 4* 58 7 6
1- 3- 4- 4* 5- 8- 9- 58 7 6
パス: 7
1- 3- 4- 4- 5- 8- 9- 58* 7 6
1- 3- 4- 4- 5- 8- 9- 58* 7 6
パス: 8
1- 3- 4- 4- 5- 8- 9- 58- 7* 6
1- 3- 4- 4- 5- 7* 8- 9- 58- 6
パス: 9
1- 3- 4- 4- 5- 7- 8- 9- 58- 6*
1- 3- 4- 4- 5- 6* 7- 8- 9- 58-
昇順にソートしました
-------------
x[0]=1
x[1]=3
x[2]=4
x[3]=4
x[4]=5
x[5]=6
x[6]=7
x[7]=8
x[8]=9
x[9]=58
疑問
- テキストだと、2分挿入ソートは安定ではないと書いているが、安定性を保持したまま実装できてる気がする
- なんか間違えてる?
回答
- 実装によりけりっぽい。ちょっと、テキストさん、断言せんといてや・・・
挿入ソートの改良で、挿入するデータの前ではソートが済んでいるという性質を利用して、挿入する箇所を二分探索するというものである。データの量が少ないときにはあまり効果がないが、多いときには比較回数が少なくなる。探索アルゴリズムによっては不安定なソートになるが、工夫により安定させることが可能である。
挿入ソート - Wikipedia
- 作者: 柴田望洋
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2007/11/07
- メディア: 単行本
- 購入: 5人 クリック: 42回
- この商品を含むブログ (19件) を見る
2014年書き初めという体で、Markdown 記法でエントリを書いてみる
このエントリの目的
気づいてなかったが、はてなブログが Markdown 記法に対応してた。
-> Markdown記法に対応しました - はてなブログ開発ブログ
Markdown 記法で書かれたドキュメントを見る機会はあるが、自分で書いてなかったので、慣れるついでに書いてみる。
このエントリ自体、Markdown 記法で記述されてるよって意味ね。
そもそも Markdown 記法ってなんぞや
ということで、こちらを読んだ。すげーわかりやすくてありがたいっすー。やったね、たえちゃん!
-> blog::2310 » Markdown文法の全訳
はてな記法とかと同じような思想で、装飾されたテキストをいかに簡便な記述から出力するか、ということに注力された記法という理解。はてな記法ともイメージが近くて、そんなに混乱することはなさそう。これが複数の媒体で横断的に使えるんだから、ありがとうってかんじやね。標準化バンザイ。
よく使いそうな記法をピックアップ
Markdown記法に対応しました - はてなブログ開発ブログ からところどころ抜粋。
見出し
- "#" でよし。はてな記法でいうところの、"*"感覚。
改行
Markdownで<br \/>タグを使用したい時は、その行の末尾を二つ以上のスペースを記述してから改行することとなります。
引用
Markdownで引用を表現するときにはEメールと同じ方法、>を用います。 もしあなたがEメールで引用をすることになじんでいるのであればMarkdownでの使用は容易です。 あなたは既にルールを知っています。 改行した各行の冒頭に>をつけるだけです。
罫線
3つ以上のハイフン(‘-’)やアスタリスク(‘*’)、アンダースコア(‘_’)だけで構成されている行は罫線<hr />となります。 また、これらの記号の間には半角スペースを用いることができます。
リスト
- はてな記法と同じ。つまり "-" や "+" を使えばよい。ただし、スペース(タブ)必須かつ、その数には意味があるので要注意。
- ネストしたい場合、はてな記法みたいに"--"ではなく、" -"みたいになるのに注意。
- 番号付きリストは、"1." とか "2." とか。
リストの番号もしくは記号は通常左端からはじまりますが、冒頭に3つのスペー スまでは許されています。またリストの番号もしくは記号の後には1つ以上の スペースか、タブが挿入されていなければいけません。
リストを綺麗に見せるために、リストの内容の二行目以降を揃えることができます。
その他
- htmlタグ等も解釈されてしまうので、タグ自体を表示させたいときは適宜エスケープするなりの処理が必要。
まとめ
ひとまずこんだけ知ってれば、はてなブログ書くのにズボラする分には、問題なさげじゃなかろうか。想像より楽ちんだった。
ソース
#このエントリの目的
気づいてなかったが、はてなブログが Markdown 記法に対応してた。
-> [http://staff.hatenablog.com/entry/2012/09/19/153219:title]
Markdown 記法で書かれたドキュメントを見る機会はあるが、自分で書いてなかったので、慣れるついでに書いてみる。
このエントリ自体、Markdown 記法で記述されてるよって意味ね。
#そもそも Markdown 記法ってなんぞや
ということで、こちらを読んだ。すげーわかりやすくてありがたいっすー。やったね、たえちゃん!
-> [http://blog.2310.net/archives/6:title]
はてな記法とかと同じような思想で、装飾されたテキストをいかに簡便な記述から出力するか、ということに注力された記法という理解。はてな記法ともイメージが近くて、そんなに混乱することはなさそう。これが複数の媒体で横断的に使えるんだから、ありがとうってかんじやね。標準化バンザイ。
#よく使いそうな記法をピックアップ
[http://staff.hatenablog.com/entry/2012/09/19/153219:title] からところどころ抜粋。
##見出し
- "#" でよし。はてな記法でいうところの、"*"感覚。
##改行
>Markdownで
タグを使用したい時は、その行の末尾を二つ以上のスペースを記述してから改行することとなります。
##引用
>Markdownで引用を表現するときにはEメールと同じ方法、>を用います。 もしあなたがEメールで引用をすることになじんでいるのであればMarkdownでの使用は容易です。 あなたは既にルールを知っています。 改行した各行の冒頭に>をつけるだけです。
##罫線
>3つ以上のハイフン(‘-’)やアスタリスク(‘*’)、アンダースコア(‘_’)だけで構成されている行は罫線\
となります。 また、これらの記号の間には半角スペースを用いることができます。
##リスト
- はてな記法と同じ。bullet は、 "-" でも "+" でも "*" でもよい。ただし、スペース(タブ)必須かつ、その数には意味があるので要注意。
- ネストしたい場合、はてな記法みたいに"--"ではなく、" -"みたいになるのに注意。
- 番号付きリストは、"1." とか "2." とか。
>リストの番号もしくは記号は通常左端からはじまりますが、冒頭に3つのスペー スまでは許されています。またリストの番号もしくは記号の後には1つ以上の スペースか、タブが挿入されていなければいけません。
リストを綺麗に見せるために、リストの内容の二行目以降を揃えることができます。
##その他
- htmlタグ等も解釈されてしまうので、タグ自体を表示させたいときは適宜エスケープするなりの処理が必要。
# まとめ
ひとまずこんだけ知ってれば、はてなブログ書くのにズボラする分には、問題なさげじゃなかろうか。想像より楽ちんだった。