こんにちは、私はに基づいて異なる値を生成することができるようにしたいICustomization
使用してISpecimenBuilder.CreateMany
。 AutoFixtureはすべてのエンティティに対して同じ値を生成するため、最善の解決策は何でしょうか。
`` `c#
パブリッククラスFooCustomization:ICustomization
{{
public void Customize(IFixtureフィクスチャ)
{{
var標本= fixture.Build
.OmitAutoProperties()
.With(x => x.CreationDate、DateTime.Now)
.With(x => x.Identifier、Guid.NewGuid()。ToString()。Substring(0、6))//個別の値を生成する必要があります
.With(x => x.Mail、$ "[email protected]")
。作成();
fixture.Register(() => specimen);
}
}
`` `
PS:主な目標はテストのコード量を減らすことなので、すべてのテストの値をカスタマイズするためにWith()
を呼び出さないようにする必要があります。 これを行う適切な方法はありますか?
更新: @ploehからのこの応答を読んだだけhttps ://github.com/AutoFixture/AutoFixture/issues/361#issuecomment-86873060それは私が探しているものかもしれません。
このアプローチには多くの欠点があります。まず、 CreateMany<Foo>
に期待する目的をやや損なうため、 Create<List<Foo>>()
を呼び出すの>(?) もう1つの欠点は、エンティティごとに2つのカスタマイズが必要になることです。 1つはカスタムコレクションを作成するためのもので、もう1つは単一のインスタンスを作成するためのものです。これは、コレクションを作成するために
Create<T>
の動作をオーバーライドしているためです。
これは、 StackOverflowの質問の良い候補のようです。 ここで質問することに何の問題もありませんが、この質問は、回答が他の人にも役立つ可能性がある種類のものであり、GitHubの問題と比較してStackOverflowで質問と回答を見つけるのが簡単だと思います。 1つは、GoogleがStack Overflowの質問のインデックス作成をうまく行っているのに対し、GitHubの問題はランク付け(クローズ)されているようです...
@ploeh私はあなたに同意します....
この問題に基づいてSO質問を作成しました。 これは将来の参考のため
@ploeh SOの質問に答えてくれてありがとう:blush:
@thiagomajesk実際、ここでの質問への回答に問題はありません。 マークがSOを提案した理由は、それが提供する検索機能です。 また、現在MarkはSOを監視しているので、そこに質問を投稿すると、彼の専門知識を得るチャンスが増えます(今回得たように):sweat_smile:
@zvirjaそれを明確にしてくれてありがとう😄
@zvirja @ploehのすばらしい回答に基づいて、SOに関する質問を締めくくります。
しかし、私はこれの実装の詳細についてもう少し情報を求めたいと思います。
私は主題からさらに逸脱したくないので、ここでこの詳細について尋ねたほうがよいと思います。 だから私はこの実装を持っています:
`` `c#
パブリッククラスUniqueShortGuidBuilder
{{
プライベート読み取り専用文字列propName;
プライベート読み取り専用int長;
public UniqueShortGuidBuilder(Expression<Func<TEntity, string>> expr, int lenght)
{
propName = ((MemberExpression)expr.Body).Member.Name;
this.lenght = lenght;
}
パブリックオブジェクトCreate(オブジェクトリクエスト、ISpecimenContextコンテキスト)
{{
var pi = PropertyInfoとしてのリクエスト;
if (pi == null || pi.PropertyType != typeof(string) || pi.Name != propName)
return new NoSpecimen();
return Guid.NewGuid().ToString().Substring(0, lenght);
}
`` `
AutoFixtureを使用して多くのオブジェクトを作成する場合、 ISpecimenBuilder.Create
が複数回呼び出され、エンティティに対して個別のGuidが生成されることを期待していました。 しかし、どうやらこれは真実ではなく、彼らは実際に同じものを共有しています。
さて、私はちょうどあなたのコードをテストしました、そしてそれは正確に望み通りに働きました:
`` `c#
パブリッククラスFoo
{{
パブリック文字列PropertyToCustomize {get; 設定; }
}
パブリッククラスUniqueShortGuidBuilder
{{
プライベート読み取り専用文字列propName;
プライベート読み取り専用int長;
public UniqueShortGuidBuilder(Expression<Func<TEntity, string>> expr, int lenght)
{
propName = ((MemberExpression) expr.Body).Member.Name;
this.lenght = lenght;
}
public object Create(object request, ISpecimenContext context)
{
var pi = request as PropertyInfo;
if (pi == null || pi.PropertyType != typeof(string) || pi.Name != propName)
return new NoSpecimen();
return Guid.NewGuid().ToString().Substring(0, lenght);
}
}
[事実]
public void TestCustomization()
{{
varfixture = new Fixture();
フィクスチャ.Customizations.Add(新しいUniqueShortGuidBuilder
var manyFoos = fixture.CreateMany<Foo>(10);
var uniqueIds = manyFoos
.Select(x => x.PropertyToCustomize)
.Where(x => x.Length == 5)
.Distinct()
.ToArray();
Assert.Equal(10, uniqueIds.Length);
}
`` `
したがって、実際のコードがどのように異なるかをもう一度確認して、その違いが重要である理由を理解できるようにしたいと思います😟
@zvirjaそれは奇妙です。 以下のコードは、私が抱えている問題の最小限の再現です。
Fixture.Customize(new FooCustomization());
を削除すると、テストは合格します。それ以外の場合は合格しません。
`` `c#
システムを使用する;
Microsoft.VisualStudio.TestTools.UnitTestingを使用する;
AutoFixture.Kernelを使用する;
System.Linq.Expressionsを使用する;
System.Reflectionを使用します。
AutoFixtureを使用します。
System.Linqを使用する;
名前空間UnitTestProject1
{{
パブリッククラスFoo
{{
パブリック文字列PropertyToCustomize {get; 設定; }
}
public class FooCustomization : ICustomization
{
public void Customize(IFixture fixture)
{
var specimen = fixture.Build<Foo>()
.OmitAutoProperties()
.With(x => x.PropertyToCustomize)
.Create();
fixture.Register(() => specimen);
}
}
public class UniqueShortGuidBuilder<TEntity> : ISpecimenBuilder
{
private readonly string propName;
private readonly int lenght;
public UniqueShortGuidBuilder(Expression<Func<TEntity, string>> expr, int lenght)
{
propName = ((MemberExpression)expr.Body).Member.Name;
this.lenght = lenght;
}
public object Create(object request, ISpecimenContext context)
{
var pi = request as PropertyInfo;
if (pi == null || pi.PropertyType != typeof(string) || pi.Name != propName)
return new NoSpecimen();
return Guid.NewGuid().ToString().Substring(0, lenght);
}
}
[TestClass]
public class UnitTest1
{
public static Fixture Fixture { get; private set; }
[ClassInitialize]
public static void ClassInitialize(TestContext context)
{
Fixture = new Fixture();
Fixture.Customizations.Add(new UniqueShortGuidBuilder<Foo>(x => x.PropertyToCustomize, 5));
Fixture.Customize(new FooCustomization());
}
[TestMethod]
public void TestMethod1()
{
var manyFoos = Fixture.CreateMany<Foo>(10);
var uniqueIds = manyFoos
.Select(x => x.PropertyToCustomize)
.Where(x => x.Length == 5)
.Distinct()
.ToArray();
Assert.AreEqual(10, uniqueIds.Length);
}
}
}
`` `
推奨事項:GUID文字列から最初の_n_文字を削除することに依存している場合は、クラスにUniqueShortGuidBuilder
という名前を付けないでください。 長さが5の場合、値はかなり一意になる可能性がありますが、1に設定すると、衝突が発生する可能性があります。 いずれにせよ、一意性は保証されません。
文字列の長さと保証されていない一意性だけを気にする場合は、AutoFixtureに0から99,999までの数値を要求して、文字列に変換してみませんか? IIRC、番号は範囲がなくなるまで一意です。
Fixture.Customize(new FooCustomization());を削除した場合テストは合格しますが、合格しません。
実は、状況をもっと注意深く見れば、それは予想される動作です😅#962の私の返信を参照しFooCustomization
実装は、 Foo
タイプのインスタンスを作成し、常にそのインスタンスを返すようにフィクスチャを構成します。 したがって、後でFoo
タイプのインスタンスを複数作成すると、同じオブジェクトが返され、ビルダーがそれ以上呼び出されなくなります。
Foo
をリクエストするたびに新しいインスタンスを作成する場合は、ここで提案しCustomize<>
APIを使用します。
@ploehヒントをありがとうございますが、そのコードはテスト目的の
文字列の長さと保証されていない一意性だけを気にする場合は、AutoFixtureに0から99,999までの数値を要求して、文字列に変換してみませんか?
これはほとんどの場合に優れたソリューションであると思いますが、システムでの英数字の検証のため、この特定のケースではそれを行うことができません。 それに加えて、私はその分野を含むより複雑な検証をしています。
実際、状況をより注意深く見直すと、それが予想される動作です。
@ zvirja #962での返信後は明らかです。 しかし、AutoFixtureに関する事前の(明示的な)ドキュメントがないため(ここで指摘し
最後に、忍耐とすべての優れた説明の両方に感謝します。
これは将来的に他の人にも役立つと確信しています😉。
さて、十分に公平です、私は物事を文字通りに取りすぎる傾向があるかもしれません😄