月別アーカイブ: 2013年12月

2013年振り返り by @masaru_b_cl

Inspired by 師走なので今年おこなったプログラマー活動を振り返る – 猫型の蓄音機は 1 分間に 45 回にゃあと鳴く

今年も残すところあとわずか!ということで、1年の活動を振り返ってみようと思います。

 


1月

TDDBC長岡開催宣言!

かねてより開催したいと言ってきたTDDBCを、長岡で開催することにし、その表明を行いました。

 


2月

LTSV祭り参戦

で一時期話題となったLTSVをdynamicに処理するライブラリ「DynamicLTSV」をC#で作成しました。

このライブラリについては、上記LTSV FAQをまとめてくださった伊藤直也さん(@naoya_ito)にも捕捉されたようで、クックパッドの宮川達彦さん(@miyagawa)との対談(Rebuild: 1: Podcasting, LTSV, RubyMotion (伊藤直也))でちょっと触れられています。これはちょっとうれしかった。以下はその文字おこしからの引用。

https://gist.github.com/akiyan/5010610

naoya C#のやつ、僕、C#は本当はほとんど見ないんですけど、ダイナミックなんとかっていう、こう、クラスを使って、型を無視して、値を扱うことができたりしていて。昔、Java書いてたときにそのウェブサービスのWeb APIとか、XMLをパースしたあとに型をマッピングさせるのがすげーめんどくさいとか思ってたんですけど。まあ最近こういう感じでダイナミックにできるんだなーとか、面白かったです、見ていて。

 


3月

共著「JavaScript ライブラリ実践活用〔厳選111〕 」発売

昨年末から執筆を始めていた共著のムックが発売されました。この仕事のおかげで、JavaScriptについて、結構詳しくなれました。また、RxJSやlinq.jsなどのMSよりのライブラリの紹介にも一役買えたのではないかと!

Uncle Bobのエントリ「The Pragmatics of TDD」翻訳

ついカッとなってやった。後悔はしていない。

TDDBC Tokyo 2013-03参加

TDDBC長岡開催に向けて、「TDDBC参加したことないんですよねぇw」って言ってたら、@katzchang@t_wadaが開いてくれたでござる。多謝!

この時は@aetos382さんとペアプロでキャフキャフしましたね。その節はお世話になりました。そしてお題がまさかの「LTSV」というwww

Niigata.LLに祝電?

3/23に開催された、LL言語に関するコミュニティ「Niigata.LL : ATND」に「祝電(?)」ということで、先日公開したDynamicLTSVについて書いたスライドを送りました。

inspireされて@neko_gata_sさんがPerlでdynamicにLTSVの処理を行うコード書いてくれました。が、それが見つからない!

 


4月

VCS(バージョンコントロールシステム)入門

弊社新人教育用にVCS入門を書きました。CC-BY-SAなので、ご自由にお使いください。また、おかしいところがあれば、IssueやPull-Request等でお知らせください。

 


5月

ムックの記事がBuild Insiderに転載される

共著「JavaScript ライブラリ実践活用〔厳選111〕 」の記事の一部がBuild Insiderに転載されました。

私が担当した分は以下の通り。

TDDBC 長岡 1.0開催

TDDBCを長岡でついに開催しました。

また、開催者だったため当日課題を行うことはできませんでしたので、後でやってみました。

ASP.NET 4.5連載開始

CodeZineでASP.NETの新機能を活用する方法を紹介する連載を始めました。

以降12月まで連載が続きました。全記事は以下の通り。

SVN入門

VCS入門と同じく新人研修のために執筆しました。

TortoiseSVNを用いてWindowsでのSVNの使い方のチュートリアルになっています。実はまだ書き終わっていなくて、今14章を書いている途中です。この章で最後の予定。

 


6月

GistSharpExtension、CreateNewGist.exeのGist API仕様変更対応

Gist APIがUser-Agent必須に仕様変更されたため、その対応を行いました。

TDDBC用C#スケルトン作成

クローンすればすぐにC#てTDDを始められる「スケルトン」を作成して公開しました。

他言語、環境のものも多数作成されているので、自分の環境にあったスケルトンを探してみてください。

 


7月

GitHubハンズオン参加

Niigata.scmの第2回イベント「GitHubハンズオン」に参加してきました。一参加者というよりは半分講師業やってましたね。WindowsのGit disに逐一反論する簡単なお仕事を主にやりましたw

このイベントのときはまだSourceTree for WindowsがGitHubと連携していなかったので、Macな方々との違いに戸惑った覚えがあります。今のバージョンでは連携できるようになってますので、Windowsな人も安心してSourceTreeでGitHubいじれます。

 


8月、9月

ASP.NET4.5連載にほぼつきっきりで、他に目立った活動はありませんでした。

 


10月

VS2013 RTM対応

VS2013がRTMとなったことにより、色々やりました。

まずはGistSharpExtensionをVS2013でも拡張機能として使えるように変更しました。

次に、TDDには欠かせない拡張機能「Quick Test Switcher」もforkしてVS2013で使えるようにしました。

この変更はPull-Requesutを送った結果、現在はオリジナルに取り込まれています。

最後に、WPF向けMVVMインフラ「Livet」をVS2013でも使えるように、簡易インストーラーを作成しました。

 


11月

「10日でおぼえるjQuery入門教室」レビュー

WINGSプロジェクトでお世話になっている山田さんのjQuery教本のレビューを行いました。

 


12月

Advent Calendar参加

毎年恒例Advent Calendarの季節到来です。私は次の4つを書きました。


 

今年は書籍、記事は結構書いていますが、blogは少なめでしたね。来年はもうちょっと色々とblogでも発信していきたいと思います。それは自分の為でもあるので。

2013年に読んだ書籍、テキストまとめ by @masaru_b_cl

今年は次のようなテキスト、書籍を読みました。備忘録的にまとめてみます。

 

アーキテクチャ関連

「アーキテクト」を目指す身として、「アーキテクチャ」の理解は欠かせないはず!ということで、今年は次の2冊を読みました。

 

パターン・ランゲージ

井庭先生(@takashiiba)のパターンに関する書籍は今年3つ読みました。

中でも「コラボレーション・パターン」がお気に入りです。仕事をチームで進める以上、コラボレーションは避ける事が出来ません。効果的なコラボレーションのやり方を今後模索していきたいと思います。

 

教育(インストラクション)

今年は新人教育担当者となったこともあり、これまで以上に「教育」というものを考えさせられた1年でした。実際に新人研修期間は、とにかく自分で思う通りにやってみたのですが、本当にこれでよかったのかという思いもあり、「教育」というものをしっかりと学んでみようと思い、こんな書籍を読みました。

教育(インストラクション)に関わる事柄が綺麗にまとまっていて、しかもより進んだ学習への道筋も「参考文献」として提示してくれていて、非常に良かったです。今後はその参考文献を中心に、より理解を深め、実践していきたいと思っています。

 

その他システム開発系

  • データベース・リファクタリング
    データベース・リファクタリング
    まだ読んでいる途中ですが、プログラムのリファクタリングと同じように、データベースの「振る舞いを変えずに構造を変える」方法について述べた書籍です。「ピアソン」の本なので、あえなく絶版となってしまっていますが、こんな動きもありますし今後の復活を願ってやみません。
  • Pro Git
    VCSのGitについて、概念から中身までざっと説明した書籍。しかも無料で読める第一資料です。
  • 実践 F# 関数型プログラミング入門

    こちらもまだ読んでいる途中です。一からF#を学ぶなら、今のところこの本以外の選択肢は無いんじゃないでしょうか?写経しながらボチボチ進めています。

 

こうやってまとめてみると、案外読んでいるものですね。来年も技術者として、常にインプットとアウトプットを心がけていくつもりです。

ASP.NET Friendly URLsを使う #aspnetjp by @masaru_b_cl

このエントリは「One ASP.NET Advent Calendar 2013 – Adventar」の参加エントリです。

前日は@NAL_6295さんの「NAL-6295の舌先三寸 » Blog Archive » [ASP.NET MVC][C#]Controllerで複数のcheckboxの状態を個々のチェック状態のBooleanではなく、チェックされているものだけの配列を引数で受け取りたい」でした。

 

Friendly URLsって?

ASP.NET WebFormsでも、シュッとしたURLを簡単に扱うためのライブラリです。

ASP.NET Friendly URLs – Home

The ASP.NET Friendly URLs library makes it easy to enable extensionless URLs for file-based handlers (e.g. ASPX, ASHX) in ASP.NET applications.

乱暴にまとめると次のような機能を提供してくれます。

  • 拡張子なしのURL
    • ~/Customer → ~/Customer.aspxにルーティング
  • 「セグメント」を使ったパラメーター指定
    • ~/Customer/1 → ~/Customer.aspx?id=1のような扱いができる

 

導入

まずは空のWebアプリケーションプロジェクトを作成しましょう。

image

「One ASP.NET」なので、「Web Forms」にチェックを入れ、「Empty」テンプレートを選択します。

image

次にNuGetからFriendlyURLsを追加します。

NuGet Gallery | Microsoft.AspNet.FriendlyUrls 1.0.2

image

 

Friendly URLsの有効化

FriendlyURLsを有効にするには、Global.asaxのApplication.Startイベントハンドラーにて、RouteConfigクラスの拡張メソッド、「EnableFriendlyUrls()」を実行します。

そして、そのための処理はNuGetで追加されたApp_Start/RouteConfigクラスのRegisterRoutesメソッドを使います。


using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Routing;
using Microsoft.AspNet.FriendlyUrls;
namespace FriendlyURLsSample
{
public static class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
var settings = new FriendlyUrlSettings();
settings.AutoRedirectMode = RedirectMode.Permanent;
routes.EnableFriendlyUrls(settings);
}
}
}

view raw

RouteConfig.cs

hosted with ❤ by GitHub


using System;
using System.Web.Routing;
namespace FriendlyURLsSample
{
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
RouteConfig.RegisterRoutes(RouteTable.Routes); // <- FriendlyURLsを有効に
}
}
}

view raw

Global.asax.cs

hosted with ❤ by GitHub

 

遷移元画面作成

遷移元画面として、Default.aspxを作成します。テキストボックスとボタンがそれぞれ一つずつある、シンプルな画面です。


<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="FriendlyURLsSample.Default" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"&gt;
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Greeting!</title>
</head>
<body>
<form id="form1" runat="server">
<div>
Enter your name:
<asp:TextBox ID="Name" runat="server"></asp:TextBox>
<asp:Button ID="Greet" runat="server" Text="Greet" OnClick="Greet_Click" />
</div>
</form>
</body>
</html>

view raw

Default.aspx

hosted with ❤ by GitHub


using System;
using Microsoft.AspNet.FriendlyUrls;
namespace FriendlyURLsSample
{
public partial class Default : System.Web.UI.Page
{
protected void Greet_Click(object sender, EventArgs e)
{
Response.Redirect(FriendlyUrl.Href("~/Greeting", Name.Text));
}
}
}

view raw

Default.aspx.cs

hosted with ❤ by GitHub

ポイントは、FriendlyUrl.Hrefメソッドです。このメソッドの第一引数に遷移先ページのURL(拡張子なし)、第二引数以降に「セグメント」として引き渡したいパラメーターを設定します。

「セグメント」については後述します。

 

遷移先画面作成

遷移先画面として、Greeting.aspxを作成します。前の画面で入力した名前を使って、あいさつ文を表示するシンプルな画面です。


<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Greeting.aspx.cs" Inherits="FriendlyURLsSample.Greeting" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"&gt;
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Greeting!</title>
</head>
<body>
<form id="form1" runat="server">
<div>
Hello! <%: Name %>
</div>
</form>
</body>
</html>

view raw

Greeting.aspx

hosted with ❤ by GitHub


using System;
using System.Linq;
using Microsoft.AspNet.FriendlyUrls;
namespace FriendlyURLsSample
{
public partial class Greeting : System.Web.UI.Page
{
public string Name { get; private set; }
protected void Page_Load(object sender, EventArgs e)
{
Name = FriendlyUrl.Segments.First();
}
}
}

この画面のポイントは、FriendlyUrl.Segmentsプロパティです。前の画面のHrefメソッドで「セグメント」に指定したパラメーターが、IList<string>型で格納されています。

今回は前の画面で入力した名前だけが欲しいので、LINQのFirstメソッドで1つだけ取得しています。

 

実行結果

では実行してみましょう。まず起動すると、名前を入力する画面が開きます。

image

名前を入力して「Greet」ボタンをクリックすると、次の画面に遷移し、入力した名前を使ってあいさつ文が表示されます。

image

image

さて、アドレスバーを見てみましょう。次のアドレスとなっています。

 

    http://localhost:3622/Greeting/TAKANO%20Sho

 

Friendly URLsの力により、”~Greeting”まででGreeting.aspx画面が表示され、その後の”/”で区切られた値がセグメントとして扱われます。FriendlyUrl.Hrefメソッドで入力した名前をセグメントに指定しているので、入力した”TAKANO Sho”がちゃんとURLエンコードされ、”TAKANO%20Sho”として設定されていることがわかります。

そして、Greeting画面では、Page_Loadイベントハンドらーにて、FriendlyUrl.Segmentsプロパティを使い、上記の”TAKANO%20Sho”というセグメントの値を、URLデコードして、”TAKANO Sho”として扱えていることが確認できます。

 

他にも……

ASP.NET 4.5で導入されたモデルバインドと組み合わせるための属性が使えたり、モバイルサイト用のページに自動でルーティングしたりといった機能があるようです。前者については、「ASP.NET スキャフォールディング」の中で使われていますので、以下の拙著記事を見てみてください。後者については良く調べていないので、前述のCodePlexのプロジェクトホームを参照してください。

ASP.NET 4.5の「Scaffolding(スキャフォールディング)」機能を試す(前編)(1/5):CodeZine

ASP.NET 4.5の「Scaffolding(スキャフォールディング)」機能を試す(後編)(1/4):CodeZine

CodeZineの記事では、もう少し詳しくFriendly URLsの動作を解説していますので、ぜひご覧ください。

 

なお、今回作成したサンプルは、GitHubにアップしてあります。

masaru-b-cl/one-asp-net-advent-calendar-2013

 

次は?

@taiga_takahariさんにバトンタッチです!

LINQ vs キーブレイク パフォーマンス勝負 #dotnetjp #adcjcs by @masaru_b_cl

はじめに

このエントリは「C# Advent Calendar 2013 – Adventar」への参加エントリです。

前日は@yone64さんの「WindowsストアアプリでReflection | 泥庭」でした。

 

LINQ vs キーブレイク

もはや空気のようになったLINQですが、いまだに活用されていない現場を見ることもあり、非常に残念に思います。

そこで、本エントリではいわゆる「キーブレイク処理」をLINQで置き換え、性能にほとんど差がないことを示し、LINQ導入の一助としたいと思います。

 

テーマ

取引先と商品ごとに単価と数量を持つ売上データを、明細、小計(商品ごと)、大計(取引先ごと)、総計を出すプログラムを考えます。

元データはこんな感じで生成します。


var q = from i in Enumerable.Range(1, 100)
from j in Enumerable.Range(1, 100)
from k in Enumerable.Range(1, 10)
select new Sales
{
Customer = string.Format("取引先{0:d2}", i),
Product = string.Format("商品{0:d2}", j),
UnitPrice = Convert.ToDecimal(j * 100),
Quantity = k
};
var source = q.ToArray();

 

キーブレイク

こんな感じで、オーソドックスなキーブレイク処理を行います。

#久々にキーブレイク処理とか書いたf(^^;


var oldCustomer = "";
var newCustomer = "";
var oldProduct = "";
var newProduct = "";
var totalQuantity = 0L;
var totalPrice = 0m;
var totalCustomerQuantity = 0L;
var totalCustomerPrice = 0m;
var totalProductQuantity = 0L;
var totalProductPrice = 0m;
foreach (var sales in source)
{
if (oldCustomer == "")
{
oldCustomer = sales.Customer;
oldProduct = sales.Product;
}
newCustomer = sales.Customer;
newProduct = sales.Product;
if (oldCustomer != newCustomer || oldProduct != newProduct)
{
Console.WriteLine("小計 P:{0}, {1}, {2}", oldProduct, totalProductQuantity, totalProductPrice);
totalProductQuantity = 0L;
totalProductPrice = 0m;
oldProduct = newProduct;
}
if (oldCustomer != newCustomer)
{
Console.WriteLine("大計 P:{0}, {1}, {2}", oldCustomer, totalCustomerQuantity, totalCustomerPrice);
totalCustomerQuantity = 0L;
totalCustomerPrice = 0m;
oldCustomer = newCustomer;
}
totalProductQuantity += sales.Quantity;
totalProductPrice += sales.UnitPrice * sales.Quantity;
totalCustomerQuantity += sales.Quantity;
totalCustomerPrice += sales.UnitPrice * sales.Quantity;
totalQuantity += sales.Quantity;
totalPrice += sales.UnitPrice * sales.Quantity;
Console.WriteLine(sales);
}
Console.WriteLine("小計 P:{0}, {1}, {2}", oldProduct, totalProductQuantity, totalProductPrice);
Console.WriteLine("大計 P:{0}, {1}, {2}", oldCustomer, totalCustomerQuantity, totalCustomerPrice);
Console.WriteLine("総計 {0}, {1}", totalQuantity, totalPrice);

 

LINQ

LINQ側はこんな感じ。取引先でグルーピングしたものをぶん回し、内部でさらに商品でグルーピングしています。もっと良い書き方があればぜひ教えていただきたいところ。


var salesOfCustomers = source.GroupBy(x => x.Customer);
foreach (var salesOfCustomer in salesOfCustomers)
{
var salesOfProducts = salesOfCustomer.GroupBy(x => (x.Product));
foreach (var salesOfProduct in salesOfProducts)
{
foreach (var sales in salesOfProduct)
{
Console.WriteLine(sales);
}
Console.WriteLine("小計 P:{0}, {1}, {2}", salesOfProduct.Key, salesOfProduct.Sum(x => x.Quantity),
salesOfProduct.Sum(x => x.UnitPrice * x.Quantity));
}
Console.WriteLine("大計 C:{0}, {1}, {2}", salesOfCustomer.Key, salesOfCustomer.Sum(x => x.Quantity),
salesOfCustomer.Sum(x => x.UnitPrice * x.Quantity));
}
Console.WriteLine("総計 {0}, {1}", source.Sum(x => x.Quantity), source.Sum(x => x.UnitPrice * x.Quantity));

view raw

UsingLinq.cs

hosted with ❤ by GitHub

 

結果

それぞれ10回ぶん回して、処理時間の平均をとってみました。その結果がこちら(単位msec)。

image

 

多少キーブレイクのほうが早い感じになってますが、誤差の範囲といってもいいんじゃないでしょうか。現に何度かやったらLINQの方がごくわずかに早いこともありました。

つまり、今回のケースではLINQを使ってもパフォーマンス上不利になることはないということです。これは、処理自体より、標準出力の方がボトルネックになってるからかなーとも思いますが、キーブレイク処理するようなときは大体帳票出したりするときなので、CPUがいくら頑張ってもI/Oが遅いはずであることを考えると、LINQを使わない理由はないかなと。コード短くて何やってるかすぐ分かりますし。

 

検証ソース

今回の検証ソースはGistにあげてあります。


using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
class Sales
{
public string Customer { get; set; }
public string Product { get; set; }
public decimal UnitPrice { get; set; }
public int Quantity { get; set; }
public override string ToString()
{
return string.Format("C:{0}, P:{1}, U:{2}, Q:{3}", Customer, Product, UnitPrice, Quantity);
}
}
static void Main(string[] args)
{
var q = from i in Enumerable.Range(1, 100)
from j in Enumerable.Range(1, 100)
from k in Enumerable.Range(1, 10)
select new Sales
{
Customer = string.Format("取引先{0:d2}", i),
Product = string.Format("商品{0:d2}", j),
UnitPrice = Convert.ToDecimal(j * 100),
Quantity = k
};
var source = q.ToArray();
const int times = 10;
var linqTime = UsingLinq(times, source);
var keyBreakTime = UsingKeyBreak(times, source);
Console.WriteLine();
Console.WriteLine("■LINQ:{0}", linqTime);
Console.WriteLine("■Key Break:{0}", keyBreakTime);
}
private static decimal UsingLinq(int times, IEnumerable<Sales> source)
{
var sw = new Stopwatch();
var totalTime = 0L;
for (var i = 0; i < times; i++)
{
sw.Reset();
sw.Start();
var salesOfCustomers = source.GroupBy(x => x.Customer);
foreach (var salesOfCustomer in salesOfCustomers)
{
var salesOfProducts = salesOfCustomer.GroupBy(x => (x.Product));
foreach (var salesOfProduct in salesOfProducts)
{
foreach (var sales in salesOfProduct)
{
Console.WriteLine(sales);
}
Console.WriteLine("小計 P:{0}, {1}, {2}", salesOfProduct.Key, salesOfProduct.Sum(x => x.Quantity),
salesOfProduct.Sum(x => x.UnitPrice * x.Quantity));
}
Console.WriteLine("大計 C:{0}, {1}, {2}", salesOfCustomer.Key, salesOfCustomer.Sum(x => x.Quantity),
salesOfCustomer.Sum(x => x.UnitPrice * x.Quantity));
}
Console.WriteLine("総計 {0}, {1}", source.Sum(x => x.Quantity), source.Sum(x => x.UnitPrice * x.Quantity));
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
totalTime += sw.ElapsedMilliseconds;
}
var averageTime = (decimal)totalTime / times;
return averageTime;
}
private static decimal UsingKeyBreak(int times, IEnumerable<Sales> source)
{
var sw = new Stopwatch();
var totalTime = 0L;
for (var i = 0; i < times; i++)
{
sw.Reset();
sw.Start();
var oldCustomer = "";
var newCustomer = "";
var oldProduct = "";
var newProduct = "";
var totalQuantity = 0L;
var totalPrice = 0m;
var totalCustomerQuantity = 0L;
var totalCustomerPrice = 0m;
var totalProductQuantity = 0L;
var totalProductPrice = 0m;
foreach (var sales in source)
{
if (oldCustomer == "")
{
oldCustomer = sales.Customer;
oldProduct = sales.Product;
}
newCustomer = sales.Customer;
newProduct = sales.Product;
if (oldCustomer != newCustomer || oldProduct != newProduct)
{
Console.WriteLine("小計 P:{0}, {1}, {2}", oldProduct, totalProductQuantity, totalProductPrice);
totalProductQuantity = 0L;
totalProductPrice = 0m;
oldProduct = newProduct;
}
if (oldCustomer != newCustomer)
{
Console.WriteLine("大計 P:{0}, {1}, {2}", oldCustomer, totalCustomerQuantity, totalCustomerPrice);
totalCustomerQuantity = 0L;
totalCustomerPrice = 0m;
oldCustomer = newCustomer;
}
totalProductQuantity += sales.Quantity;
totalProductPrice += sales.UnitPrice * sales.Quantity;
totalCustomerQuantity += sales.Quantity;
totalCustomerPrice += sales.UnitPrice * sales.Quantity;
totalQuantity += sales.Quantity;
totalPrice += sales.UnitPrice * sales.Quantity;
Console.WriteLine(sales);
}
Console.WriteLine("小計 P:{0}, {1}, {2}", oldProduct, totalProductQuantity, totalProductPrice);
Console.WriteLine("大計 P:{0}, {1}, {2}", oldCustomer, totalCustomerQuantity, totalCustomerPrice);
Console.WriteLine("総計 {0}, {1}", totalQuantity, totalPrice);
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
totalTime += sw.ElapsedMilliseconds;
}
var averageTime = (decimal)totalTime / times;
return averageTime;
}
}
}

view raw

Program.cs

hosted with ❤ by GitHub

 

次は?

MS MVPの@hiroyuki_moriさんです!

デリゲート~ラムダ式の歴史を辿って | もりひろゆきの日々是勉強

テストケースの分類法あれこれ in C# with MSTest #TddAdventJp #TDD by @masaru_b_cl

はじめに

本エントリは「TDD Advent Calendar 2013」の参加エントリです。

8日目は@biacさんの「TDD 最初の一歩 (C#編) #tddadventjp: TDD.NET」でした。

 

テストケースの分類

TDD限らず自動化テストを行っていると、どんどん増えていくテストケースをいかに整理、管理していくかが課題の一つとなってきます。整理できていないと、テストコードの可読性低下、不要なテストが残ることによるテストの実行速度低下、思いがけないレッド(=テスト失敗)などにつながります。

本エントリではC#およびMSTestを使い、「xxがxxの場合、xxがxxならばxxとなる」を表すテストケースの分類方法をまとめてみます。

 

テストケース名での分類

一番シンプルなのが、テストケース名で分類する方法です。

 


using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace TddAdventJp2013.Test
{
[TestClass]
public class テストケース名
{
[TestMethod]
public void Aがa1の場合・Bがb1ならばc1となる()
{
}
[TestMethod]
public void Aがa1の場合・Bがb2ならばc2となる()
{
}
[TestMethod]
public void Aがa2の場合・Bがb1ならばc3となる()
{
}
}
}

 

メリット

  • なんといってもシンプル

デメリット

  • 視認性がいまいち
    image
  • 「、」がテストケース名に使えないので「・」で代用しないといけない
  • テストケース名がいわゆる半角数字で始められないなど、C#の言語仕様に制限される

 

カテゴリーを使った分類

カテゴリーをつけるための属性を使用する方法です。

 


using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace TddAdventJp2013.Test
{
[TestClass]
public class カテゴリー
{
[TestCategory("Aがa1の場合")]
[TestMethod]
public void Bがb1ならばc1となる()
{
}
[TestCategory("Aがa1の場合")]
[TestMethod]
public void Bがb2ならばc2となる()
{
}
[TestCategory("Aがa2の場合")]
[TestMethod]
public void Bがb1ならばc3となる()
{
}
}
}

 

メリット

  • テストケース名がシンプルになる
  • テストエクスプローラーで、カテゴリーごとに表示できる
    image
  • ファイルを横断してテストケースを分類できる

デメリット

  • 同じカテゴリーを何度も指定しないといけない
  • カテゴリーごとに表示しても階層は2階層止まり

 

内部クラスを使った分類

テストケースを内部クラスとして定義する方法です。

 


using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace TddAdventJp2013.Test
{
public class 内部クラス
{
[TestClass]
public class Aがa1の場合
{
[TestMethod]
public void Bがb1ならばc1となる()
{
}
[TestMethod]
public void Bがb2ならばc2となる()
{
}
}
[TestClass]
public class Aがa2の場合
{
[TestMethod]
public void Bがb1ならばc3となる()
{
}
}
}
}

 

メリット

  • テストケース名がシンプルになる
  • 「~の場合」といった内容を一度だけ書けばよい
  • テストエクスプローラーで、内部クラスごとに表示できる
    image
  • 複数階層もいける
  • 別の分類で同じテストケース名が使える

デメリット

  • 少々面倒
  • 内部クラス名、テストケース名がいわゆる半角数字で始められないなど、C#の言語仕様に制限される

 

番外:partialクラスの活用

C#はpatialクラスを使うことで、一つのクラスを複数ファイルに分割することができます。これを利用することで、同一テストクラスでも意味のあるまとまりごとにテストケースを分けて記載することが可能になります。

image

 


using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace TddAdventJp2013.Test
{
[TestClass]
public partial class partialクラス
{
}
}

 


using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace TddAdventJp2013.Test
{
public partial class partialクラス
{
[TestCategory("Aがa1の場合")]
[TestMethod]
public void Bがb1ならばc1となる()
{
}
[TestCategory("Aがa1の場合")]
[TestMethod]
public void Bがb2ならばc2となる()
{
}
}
}

 


using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace TddAdventJp2013.Test
{
public partial class partialクラス
{
[TestCategory("Aがa2の場合")]
[TestMethod]
public void Bがb1ならばc3となる()
{
}
}
}

 

最後に

テストケースを整理するために上にあげた方法は排他的なものではないので、それぞれ組み合わせることももちろん可能です。他にももっと良い分類方法などあれば、ぜひ教えてください。

なお、今回のサンプルコードは、GitHubにあげてあります。

https://github.com/masaru-b-cl/TDDAdventJp2013

 

次は

10日目はまだ空いているようです。そこのあなた!ぜひ書いてみてはいかがでしょう!

参加登録は「TDD Advent Calendar 2013の参加状況確認・参加登録 – Qiita [キータ]」から!

[2013/12/11追記]
10日目がアップされました! > @Lewuatheさん++

Agile Samurai Basecamp in Tokyo – The first cry of Atom

F# InteractiveをConEmu上で利用する #FsAdventJP #fsharp by @masaru_b_cl

はじめに

本エントリは「F# Advent Calendar 2013」の参加エントリです。

前日はF# MVPである@pocketberserkerさん……のはずなのですが、このエントリ書いている段階ではまだ公開されていないようです。ちなみに、一昨日は@gab_kmさんの「MailboxProcessor に処理をさせよう」でした。

[2013/12/10追記]

5日めのエントリ、アップされました!!!

「Javaで継続モナド」をF# に翻訳 – pocketberserkerの爆走

 

F#の対話環境「F# Interactive」

F#には、学習やちょっとしたコードを試したいときなどに便利な対話環境が用意されています。それが「F# Interactive」です。F# Interactiveを使うと、ビルド等の手間を省いて入力したコード片を即座に実行できるため、動きを確認しながら作業をしたい場合にはうってつけです。

このF# Interactive、通常はVisual Studioを起動して「表示」メニュー→「その他のウィンドウ」→「F# Interactive」として起動して使います(メニューを見るとショートカットキーCtrl+Alt+Fでも起動できるみたいです)。

image

image

ただ、いちいちVisual Studioを立ち上げないといけないのは面倒ですよね。

 

「F# Interactive」の実体「fsi.exe」

ではどうするか?答えは簡単で、「直接F# Interactiveを起動する」です。その際に使用するのが、F# Interactiveの実体である実行プログラム「fsi.exe」です。

fsi.exeは %ProgramFiles%(64bitなWindowsなら%ProgramFiles(x86)%)\Microsoft SDKs\F#\3.1\Framework\v4.0\Fsi.exe にあります。インストールしてあるVisual Studioのバージョンによっては、”3.1”の部分が違っていることもあるでしょうが、だいたいこの辺りにあります。

image

このfsi.exeをダブルクリックして実行すると、コマンド プロンプトで「F# Interactive」が起動します。

image

ショートカットやスタートスクリーン(メニュー)、タスクバーなどへのピン止めをしておけば、あっという間にF# Interactiveが使えて便利です!

 

F# InteractiveをConEmu上で利用する

ようやく本題です。便利なF# Interactiveですが、コマンド プロンプトでは少し味気ないですよね。そこで登場するのが、各種の「コンソールエミュレーター」です。今回はそれら中でも、「ConEmu」上でF# Interactiveを動かしてみます。

なお、余談ですが。私はつい最近まで「Console2」を使っていました。ただ、日本語を使うには野良ビルドされたバイナリで置き換える必要があったり、開発がそれほど活発ではありませんでした。それがConEmuでは解消されていますし、結構細かなカスタマイズも出来るため気に入っています(あと、Windowsではうまく表示できないという評判の「Ricty」フォントも、アンチエイリアスを「Standard」にすることで、結構綺麗に表示できます)。

では、本題に戻り、ConEmuの設定方法を見て行きましょう。まず、ConEmuの設定画面を出します。タイトルバーを右クリックしてコンテキストメニューから「Settings…」を選択します。

image

設定画面が表示されるので、左のツリーから「Startup」→「Tasks」を選択します。

image

「+」ボタンをクリックしてタスクを追加後、「Task name」に”F# Interactive”、「Task Parameters」に”/dir (fsi.exeのあるフォルダ)”、「Commands」に”fsi.exe”を入力して、「Save settings」ボタンをクリックします。

image

あとは、ConEmu上の「+」ボタンから、登録したF# Interactiveを選択して起動します。

image

image

これで、少しかっちょいいF# Interactiveの完成です!

 

まとめ

  • F#は対話環境として「F# Interactive」がある
    • 通常はVisual Studioの一つのウィンドウとして使用できる
    • いちいちVS立ち上げるのめんどくさい
  • F# Interactiveの実体は、fsi.exe
    • 置き場所は %ProgramFiles%\Microsoft SDKs\F#\3.1\Framework\v4.0\Fsi.exe あたり
  • ConEmu等のコンソールエミュレーターでfsi.exeを実行すれば、ちょっとかっこいいF# Interactiveができる
    • これで快適なF# Interactiveライフを!

 

次は?

@brother_nomuさんの「F# で Oracle にアクセスしてみたよ! – nomurabbitの日記」です。

実例で学ぶASP.NET 4.5 Webフォーム 新機能活用法:第5回と第6回が公開されています

ASP.NET 4.5の「Scaffolding(スキャフォールディング)」機能を試す(前編)

ASP.NET 4.5の新機能「Unobtrusive Validation(控えめな検証)」「async/awaitを用いた非同期ページ」

 

またしてもご無沙汰していましたが、CodeZineで公開中です。

バックナンバーはこちら↓

実例で学ぶASP.NET 4.5 Webフォーム 新機能活用法:CodeZine(コードジン)