PrimitiveAssert は対象をプリミティブ データ型に分解してアサートするシンプルなライブラリです。
- .NET Standard 2.0
- .NET Framework 4.6.1
このライブラリは、任意のデータ型に対して統一された API によって等値アサートを行う事を目的としています。
基本的な使い方は次の通りです:
actual.AssertIs(expected);
PrimitiveAssert.AssertIs()
拡張メソッドは、actual と expected の public かつ instance なデータ メンバーを等値アサート可能な最小単位 "プリミティブ データ" に分解し、比較します。
例えば、下記のようなデータ型 IAccount
を返すメソッドをテストする場合:
public interface IAccount {
int AccountId { get; }
string Name { get; }
DateTime CreatedAt { get; }
IReadOnlyCollection<string> Tags { get; }
}
public class FakeAccountQuery {
public IAccount Find(int accountId) {
return new Account {
AccountId = accountId,
Name = "John Smith",
CreatedAt = new DateTime(2020, 3, 9, 8, 15, 0),
Tags = new []{ "Foo", "Bar" },
};
}
private sealed class Account : IAccount {
public int AccountId { get; set; }
public string Name { get; set; }
public DateTime CreatedAt { get; set; }
public IReadOnlyCollection<string> Tags { get; set; }
}
}
次のように等値アサートが書けます:
var query = new FakeAccountQuery();
var actual = query.Find(123);
actual.AssertIs(new {
AccountId = 123,
Name = "John Smith",
CreatedAt = new DateTime(2020, 3, 9, 8, 15, 0),
Tags = new []{ "Foo", "Bar" },
});
ここでは expected に匿名型を用いましたが、もちろん IAccount
を継承した型を使用する事もできます:
var query = new FakeAccountQuery();
var actual = query.Find(123);
actual.AssertIs(new StubAccount {
AccountId = 123,
Name = "John Smith",
CreatedAt = new DateTime(2020, 3, 9, 8, 15, 0),
Tags = new[] { "Foo", "Bar" },
});
...
class StubAccount : IAccount {
public int AccountId { get; set; }
public string Name { get; set; }
public DateTime CreatedAt { get; set; }
public IReadOnlyCollection<string> Tags { get; set; }
}
どのようにアサートが行われているか、その詳細なログが必要な場合は
PrimitiveAssert.ConsoleLogging = true;
とする事で、次のようなログがコンソールに出力されます:
actual: IAccount = {
AccountId: Int32 = 123 // actual と expected は数値型として等しいです。
Name: String = "John Smith" // actual と expected は String 型として等しいです。
CreatedAt: DateTime = "2020/03/09 8:15:00" // actual と expected は DateTime 型として等しいです。
Tags: IReadOnlyCollection<String> = {
[0]: String = "Foo" // actual と expected は String 型として等しいです。
[1]: String = "Bar" // actual と expected は String 型として等しいです。
}
}
比較対象となるデータ メンバーは、public かつ instnace なプロパティ及びフィールドが選ばれますが、時には比較対象から外したいデータ メンバーがあるかも知れません。
interface IFoo {
string Foo { get; }
}
interface IBar {
string Bar { get; }
}
class FooBar : IFoo, IBar {
public string Foo { get; set; }
public string Bar { get; set; }
}
...
var fooBar = new FooBar {
Foo = "Foo1",
Bar = "Bar2",
};
// IFoo として等値アサートしたいが、これだと PrimitiveAssertFailedException がスローされる
fooBar.AssertIs(new {
Foo = "Foo1",
});
そのような場合はターゲット型を明示する事で対応が可能です:
// IFoo として等値アサートされる。PrimitiveAssertFailedException はスローされない
fooBar.AssertIs<IFoo>(new {
Foo = "Foo1",
});
// or
fooBar.AssertIs(typeof(IFoo), new {
Foo = "Foo1",
});
ターゲット型省略時は、actual の変数型がターゲット型となります。従って以下の記述でも IFoo
の等値アサートは可能です:
IFoo fooBar = new FooBar {
Foo = "Foo1",
Bar = "Bar2",
};
// fooBar.AssertIs<IFoo> と同じ
fooBar.AssertIs(new {
Foo = "Foo1",
});
また、actual はターゲット型を実装してる必要はなく、ダック タイピング的にデータ メンバーを備えていれば問題ありません。
例えば匿名型を使用して、任意のデータ メンバーのみを等値アサートの対象とする荒業もやろうと思えばできます:
var query = new FakeAccountQuery();
var actual = query.Find(123);
var targetType = new {
Name = default(string),
}.GetType();
// Name プロパティのみを等値アサートの対象としている
actual.AssertIs(targetType, new {
Name = "John Smith",
});