通常Visual Studioでテスト可能なのはpublicに公開されているメソッドのみであり、internal以上のアクセス制限がかかったメソッドに対してコンテキストメニューから「単体テストの作成」を選んでも「単体テストの作成は、パブリック クラスかパブリック メソッド内でのみサポートされます」とアラートが出てテストを作成することができない。
しかし、privateメソッドだけならまだしもプログラム内で他のクラスから呼ばれる可能性のあるinternalメソッドに関してはテストを行いたい場合があるのが実情だろう。
Visual Studioではテストを行いたいメソッドが所属するプロジェクトのAssemblyInfoにInternalsVisibleToを追加することによって、特定のプロジェクト(今の場合テストプロジェクト)に対して、internalメソッドの利用を許可することが可能である。
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// アセンブリに関する一般情報は以下の属性セットをとおして制御されます。
// アセンブリに関連付けられている情報を変更するには、
// これらの属性値を変更してください。
[assembly: AssemblyTitle("Test Target")]
[assembly: AssemblyDescription("")]
#if DEBUG
[assembly: InternalsVisibleTo("TestProject")]
#endif
このように記述することで、internalメソッドを単体テストから呼ぶことが可能になる。ただし、コンテキストメニューからの作成はやはりできないのでテストクラスの作成などは自前で行う必要がある場合がある。
ここで#if DEBUGで属性をくくっているのは、テストのときだけこの属性が用いられ、リリース時には不要なアセンブリへの公開をしないための配慮である。
これで話が完了するのであれば、internalメソッドのテスト法などで検索することで情報を簡単に取得することができるのだが、テスト対象プロジェクトが「厳密な名前の署名つきアセンブリ」であった場合「フレンドアセンブリ参照''は無効です。厳密な名前の署名つきアセンブリはそのInternalsVisibleTo宣言内で公開キーを指定しなければなりません」とアラートが表示されビルドエラーが出てしまう。
これは、はテスト対象プロジェクトが「厳密な名前の署名つきアセンブリ」であった場合はフレンドアセンブリも「厳密な名前の署名つきアセンブリ」である必要があるという制限があるかららしい。
では、どのようにすればテストプロジェクトに厳密な名前の署名を付けることができるのだろうか?
一番簡単な方法は、すでに使われているテスト対象プロジェクトで使われている「厳密な名前のキー ファイル」を指定して、テストプロジェクトに署名をしてしまう方法である。(「厳密な名前のキーファイル」の作成から手順を踏みたい場合はhttps://docs.microsoft.com/ja-jp/dotnet/standard/assembly/sign-strong-name を参考)
TestTargetプロジェクトのキーファイル(myproj_key.sn)をテストプロジェクトのフォルダにコピーしてきて、テストプロジェクトのプロパティを開き、署名タブで署名にチェックを入れれば、テストプロジェクトは「厳密な名前の署名つきアセンブリ」となる。
次に、テストプロジェクトの公開キーを取得する必要があるが、これにはVisalStudioの「開発者コマンドプロンプト」を用いる。
いったん、InternalsVisibleTo属性をコメントアウトするなどしテストプロジェクトをビルド後、生成されたテストプロジェクトのdllに対して
sn -Tp <TestProject>.dll
上記のような結果が得られる。この「公開キー」の部分をAssemblyInfoで指定すれば、テストプロジェクトでinternalメソッドを利用することが可能となる。
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// アセンブリに関する一般情報は以下の属性セットをとおして制御されます。
// アセンブリに関連付けられている情報を変更するには、
// これらの属性値を変更してください。
[assembly: AssemblyTitle("Test Target")]
[assembly: AssemblyDescription("")]
#if DEBUG
[assembly: InternalsVisibleTo("TestProject, PublicKey=<TestProjectの公開キー>")]
#endif
部分ごとには検索に引っかかる情報なのだが、まとまって記されている記事が目につかなかったのでまとめてみた。