はじめに
Unit Test を書く際に private や internal なクラスのインスタンスを作りたくなることがあります。
(internal については自分で書いたライブラリであれば InternalsVisibleTo
でテスト用のプロジェクトを指定すればテストが簡単になる)
この時、Generics ではない普通のクラスであれば Assembly
の GetType
メソッドにクラス名を指定してあげることで型情報を取得することができます。
しかし Generics の場合、型をパラメータとして与えることができるため、型情報を取得するときに名前の指定方法を少し気を付ける必要があるのでその情報をまとめます。
なおこのサンプルで使用する Unit Test のライブラリは xUnit を使用していることとします。
方法
例えば Sample.csproj の Sample.cs に以下のような Sample
クラスが定義されていたとします。
namespace Sample
{
class Foo<TBar, TBaz>
{
public TBar Bar { get; set; }
public TBaz Baz { get; set; }
}
}
このクラスをテストプロジェクトである Sample.Tests.csproj でインスタンス化するには以下のようにします。
using System:
using System.Reflection;
using Xunit;
namespace Sample.Tests
{
public class Bar
{
}
public class Baz
{
}
public class FooTest
{
[Fact]
public void Test()
{
// Hoge は Sample.csproj で public なクラス
// テスト対象の Assembly を取得する方法はいくつかあるがライブラリのテストであれば
// プロジェクトの参照を持っているだろうし public なクラスもあるはずなので GetAssembly で取得するのが楽
var asm = Assembly.GetAssembly(typeof(Hoge));
// Generics の場合はパラメータとして与えられた型の個数を「`」の後に指定する
// そして具体的に Generics のパラメータにどのような型を指定するかは
// 型の最後に[[{型の名前1}],[{型の名前2}],...,[{型の名前n}]]のように指定する
var type = asm.GetType("Sample.Foo`2[[Sample.Tests.Bar, Sample.Tests],[Sample.Tests.Bar, Sample.Tests]]");
// 取得した type を使用して CreateInstance でインスタンスを作成する
var instance = Activator.CreateInstance(type);
Assert.NotNull(instance);
}
}
}
また、 type
を取得するには以下のような方法もあります。
var type = asm.GetType("Sample.Foo`2").MakeGenericType(typeof(Bar), typeof(Baz));
この方法は Generics で指定する型のパラメータがテストプロジェクトからアクセスできる場合に有効です。
Intellisense や IDE のリファクタリング機能が作用するので使用できるのであればこちらのほうがおすすめです。
Foo
が Nested Type の場合
Foo
が別の Generics Type の Nested Type の場合もあるかもしれません。
namespace Sample
{
class Qux<TQuux>
{
private class Foo<TBar,TBaz>
{
public TBar Bar { get; set; }
public TBaz Baz { get; set; }
public TQuux Quux { get; set; }
}
}
}
この場合に type
を取得する場合は以下のようにします。
// Nested Type の場合 Declaring Type の後に「+」を付けてそのあとに名前を指定します
// Generics の具体的な型の指定は Declaring Type も Nested Type もまとめて配列で指定します
var type = asm.GetType("Sample.Qux`1+Foo`2[[Sample.Tests.Quux, Sample.Tests],[Sample.Tests.Bar, Sample.Tests],[Sample.Tests.Baz, Sample.Tests]]");
または
var type = asm.GetType("Sample.Qux`1+Foo`2").MakeGenericType(typeof(Quux), typeof(Bar), typeof(Baz));
このように private な Generics 型でもインスタンス化できるので Unit Test で今までテストができていなかったところもテストできるようになります。
Sample Source
今回のサンプルを GitHub に上げました。
generics-type-instance-creating-sample