中さんのブログを見ていて、C# 2.0に決定的な見落としがあることに気付きました。
これは、即座に存在に気付いてリアクションすべきだった機能です。他のどのような機能強化よりも、私にとっては重要です。
いろいろ時間を取られて目が行き届いていないのがバレバレ (汗。
問題・単体テストとinternal §
プログラムの複雑度を低減するという観点から言えば、あらゆる箇所から参照できる"public"の利用は、全く好ましいことではありません。
これは、インテリセンスで表示される候補が不必要に増えるという意味でも生産性を低下させる要因となります。
C#において、これを解決するための手段は十分にあるとは言えません。
強いて言えば、"public"ではなく、"internal"が解決策となります。これを使うことで、特定アセンブリ内からのみアクセスを許可することを明示できます。
ところが、"internal"は、単体テスト(テスト駆動開発)との併用で決定的な問題を引きおこします。テストメソッドは、通常、別のアセンブリ上に記述しますが、"internal"スコープ上のクラスやメソッドを呼び出せないのです。
解決・InternalsVisibleToAttribute §
InternalsVisibleToAttributeを使うと、指定したアセンブリに対してのみ、"internal"スコープのクラスやメソッドを公開することができます。
これを使うことで、テスト・アセンブリにのみアクセスを許す"internal"スコープのクラスやメソッドを記述できます。
サンプルソース §
NUnitを用いて、実際にInternalスコープのメソッドをテストする例です。
Visual Studio 2005を用いて、C#の2つのクラスライブラリのプロジェクトを含むソリューションを作成します。
テスト対象のクラス §
using System;
using System.Collections.Generic;
using System.Text;
namespace NUnitAndFrientAssembly001
{
internal class ClassTarget
{
internal int Add(int x, int y)
{
return x + y;
}
}
}
テストを行うクラス §
(注: テスト対象のクラスを含むプロジェクトと、nunit.framework.dllへの参照を追加する必用があります)
using System;
using System.Collections.Generic;
using System.Text;
using NUnit.Framework;
using NUnitAndFrientAssembly001;
namespace TestNUnitAndFrientAsembly001
{
[TestFixture]
public class TestClassTaregt
{
[Test]
public void TryTestAdd()
{
ClassTarget t = new ClassTarget();
Assert.AreEqual(3, t.Add(1, 2));
}
}
}
テスト対象のクラスを含むプロジェクトのAssemblyInfo.csに追加した行 §
[assembly: InternalsVisibleTo("TestNUnitAndFrientAsembly001")]
※ 上記の行を入れないと、ビルドが失敗します。つまり、それがInternalsVisibleToAttributeの効能です。
感想 §
いいぞ。この機能は好きです。
100点満点にはほど遠いですが、アセンブリをカプセル化の単位として扱いつつ、アセンブリ内の隠されたクラスを単体テストの対象にできます。
Genericsの価値を1とすれば、この機能の価値は(私にとって)100ぐらいにあたります。
既存のC#ソース資産に対して、今すぐ使える改善策としては、十分すぎるほど優秀です。
単にこれを使うために、Visual Studio.NET 2003ベースのソースを、早急に2005ベースに移行しても良いぐらい好き。
ちなみに、C#で可能ということは、他の言語はどうだろうと思って軽く見てみると、VBでは無理っぽい? C++/CLIでは、#using "……" as_friendのような構文を併用しつつ使える?
残念ながら時間がないので、この場はここまで!