AutoFixture.AutoMoq пастаўляць вядомае значэнне для аднаго параметру канструктара

Я толькі пачаў выкарыстоўваць <�моцны> AutoFixture.AutoMoq у маіх модульных тэстаў, і я знаходжу гэта вельмі карысна для стварэння аб'ектаў, дзе я не клапаціцца аб канкрэтным значэнні. У рэшце рэшт, стварэнне ананімнага аб'екта з'яўляецца тое, што гэта ўсё аб.

Тое, што я змагаюся з, калі я клапачуся пра аднаго ці некалькіх параметраў канструктара. Возьмем ExampleComponent ніжэй:

public class ExampleComponent
{
    public ExampleComponent(IService service, string someValue)
    {
    }
}

Я хачу напісаць тэст, дзе я паставіць пэўнае значэнне для SomeValue , але пакінуць IService будзе створаны аўтаматычна <�моцны> AutoFixture.AutoMoq .

Я ведаю, як выкарыстоўваць Фіксаваць на мой IFixture , каб трымаць вядомае значэнне, якое будзе ўпырскваецца ў кампанент, але я не магу дастаткова ўбачыць, як харчавання вядомае значэнне маёй.

Вось тое, што я б у ідэале хацеў бы зрабіць:

[TestMethod]
public void Create_ExampleComponent_With_Known_SomeValue()
{
   //create a fixture that supports automocking
    IFixture fixture = new Fixture().Customize(new AutoMoqCustomization());

   //supply a known value for someValue (this method doesn't exist)
    string knownValue = fixture.Freeze("My known value");

   //create an ExampleComponent with my known value injected 
   //but without bothering about the IService parameter
    ExampleComponent component = this.fixture.Create();

   //exercise component knowning it has my known value injected
    ...
}

Я ведаю, што я мог бы зрабіць гэта з дапамогай выкліку канструктара наўпрост, але гэта ўжо не будзе мець стварэнне ананімнага аб'екта. Ці ёсць спосаб выкарыстоўваць <�моцны> AutoFixture.AutoMock , як гэта, ці мне трэба, каб уключыць DI кантэйнер ў маіх тэстах, каб мець магчымасць рабіць тое, што я хачу?


<�Моцны> EDIT:

Я, верагодна, павінен быў быць менш у маім абстрактным арыгінальным пытанні, так вось мой канкрэтны сцэнар.

I have an ICache interface which has generic TryRead and Write methods:

public interface ICache
{
    bool TryRead(string key, out T value);

    void Write(string key, T value);

   //other methods not shown...  
}

Я рэалізацыя CookieCache , дзе ITypeConverter апрацоўвае пераўтварэнні аб'ектаў у радкі і з радкоў і працягласць жыцця выкарыстоўваецца, каб усталяваць тэрмін прыдатнасці печыва.

public class CookieCache : ICache
{
    public CookieCache(ITypeConverter converter, TimeSpan lifespan)
    {
       //usual storing of parameters
    }

    public bool TryRead(string key, out T result)
    {
       //read the cookie value as string and convert it to the target type
    }

    public void Write(string key, T value)
    {
       //write the value to a cookie, converted to a string

       //set the expiry date of the cookie using the lifespan
    }

   //other methods not shown...
}

Таму пры напісанні тэсту на дату заканчэння тэрміну дзеяння печыва, я клапачуся аб працягласці жыцця, але не так шмат пра пераўтваральніку.

20
Ну, гэта даволі цяжка для мяне, каб сказаць, таму што я не бачу, як вы збіраецеся выкарыстоўваць працягласць жыцця . Не працягласць жыцця павінен ўзаемадзейнічаць з бягучым часам? Як толькі вы пачынаеце думаць аб такіх пытаннях, як тыя, магчыма, абстракцыя па-ранейшаму ўзнікае. У апошні раз, калі я зрабіў нешта накшталт гэтага, я прыбыў у інтэрфейс ILease замест гэтага, які зрабіў лагічны кэш значна больш гнуткім, паколькі цяпер я мог падтрымліваць: Absolute Заканчэнне тэрміну, Рассоўныя вокны, Заканчэнне тэрміну LRU Заканчэнне тэрміну, а таксама мноства іншых варыянтаў.
дададзена аўтар Mark Seemann, крыніца
Ну, гэта даволі цяжка для мяне, каб сказаць, таму што я не бачу, як вы збіраецеся выкарыстоўваць працягласць жыцця . Не працягласць жыцця павінен ўзаемадзейнічаць з бягучым часам? Як толькі вы пачынаеце думаць аб такіх пытаннях, як тыя, магчыма, абстракцыя па-ранейшаму ўзнікае. У апошні раз, калі я зрабіў нешта накшталт гэтага, я прыбыў у інтэрфейс ILease замест гэтага, які зрабіў лагічны кэш значна больш гнуткім, паколькі цяпер я мог падтрымліваць: Absolute Заканчэнне тэрміну, Рассоўныя вокны, Заканчэнне тэрміну LRU Заканчэнне тэрміну, а таксама мноства іншых варыянтаў.
дададзена аўтар Mark Seemann, крыніца
Чаму вы хочаце гэта зрабіць? Што такое сцэнар? IME, сцэнары, як гэта, як правіла, пахнуць змешаных праблем у ExampleComponent . Там прычына AutoFixture не падтрымлівае гэта з скрынкі.
дададзена аўтар Mark Seemann, крыніца
@MarkSeemann Мне падабаецца гук ад ILease, і я думаю, што я павінен выпраўлюся, калі я сказаў, што маё рашэнне не можа быць вытлумачаны як змешаныя праблемы. Паколькі рэдагаванне пытанне я дадаў IDateTimeProvider залежнасць да майго CookieCache і я ўсталяваць дату заканчэння тэрміну дзеяння печыва, дадаўшы працягласць жыцця таку дата. Цяпер я разумею, што гэта на самай справе з'яўляецца змяшаным неспакой, нягледзячы на ​​тое, што змяшанае неспакой толькі ўзяў адзін радок кода!
дададзена аўтар Nick Soper, крыніца
@MarkSeemann Мне падабаецца гук ад ILease, і я думаю, што я павінен выпраўлюся, калі я сказаў, што маё рашэнне не можа быць вытлумачаны як змешаныя праблемы. Паколькі рэдагаванне пытанне я дадаў IDateTimeProvider залежнасць да майго CookieCache і я ўсталяваць дату заканчэння тэрміну дзеяння печыва, дадаўшы працягласць жыцця таку дата. Цяпер я разумею, што гэта на самай справе з'яўляецца змяшаным неспакой, нягледзячы на ​​тое, што змяшанае неспакой толькі ўзяў адзін радок кода!
дададзена аўтар Nick Soper, крыніца
@MarkSeemann што вы думаеце аб маім сцэнары ў адрэдагаванае пытанні? Я не думаю, што гэта можа быць інтэрпрэтавана як змешаныя праблемы.
дададзена аўтар Nick Soper, крыніца
@MarkSeemann што вы думаеце аб маім сцэнары ў адрэдагаванае пытанні? Я не думаю, што гэта можа быць інтэрпрэтавана як змешаныя праблемы.
дададзена аўтар Nick Soper, крыніца

10 адказы

Так што я ўпэўнены, што людзі маглі б выпрацаваць абагульненую рэалізацыю прапановы Маркі, але я думаў, што я адпраўлю яго для каментароў.

Я стварыў агульны ParameterNameSpecimenBuilder заснаваны на Марка LifeSpanArg :

public class ParameterNameSpecimenBuilder : ISpecimenBuilder
{
    private readonly string name;
    private readonly T value;

    public ParameterNameSpecimenBuilder(string name, T value)
    {
       //we don't want a null name but we might want a null value
        if (string.IsNullOrWhiteSpace(name))
        {
            throw new ArgumentNullException("name");
        }

        this.name = name;
        this.value = value;
    }

    public object Create(object request, ISpecimenContext context)
    {
        var pi = request as ParameterInfo;
        if (pi == null)
        {
            return new NoSpecimen(request);
        }

        if (pi.ParameterType != typeof(T) ||
            !string.Equals(
                pi.Name, 
                this.name, 
                StringComparison.CurrentCultureIgnoreCase))
        {
            return new NoSpecimen(request);
        }

        return this.value;
    }
}

Я тады вызначыў агульны FreezeByName <�код /> метад пашырэння IFixture , які ўсталёўвае налады:

public static class FreezeByNameExtension
{
    public static void FreezeByName(this IFixture fixture, string name, T value)
    {
        fixture.Customizations.Add(new ParameterNameSpecimenBuilder(name, value));
    }
}

Наступны тэст цяпер будзе праходзіць:

[TestMethod]
public void FreezeByName_Sets_Value1_And_Value2_Independently()
{
    //// Arrange
    IFixture arrangeFixture = new Fixture();

    string myValue1 = arrangeFixture.Create();
    string myValue2 = arrangeFixture.Create();

    IFixture sutFixture = new Fixture();
    sutFixture.FreezeByName("value1", myValue1);
    sutFixture.FreezeByName("value2", myValue2);

    //// Act
    TestClass result = sutFixture.Create>();

    //// Assert
    Assert.AreEqual(myValue1, result.Value1);
    Assert.AreEqual(myValue2, result.Value2);
}

public class TestClass
{
    public TestClass(T value1, T value2)
    {
        this.Value1 = value1;
        this.Value2 = value2;
    }

    public T Value1 { get; private set; }

    public T Value2 { get; private set; }
}
20
дададзена
Працаваў як шарм. Зробленая крыху гібрыдная перагрузка, калі замарожанае значэнне па-ранейшаму генеруюцца аўтаматычна: адкрытага статычны Т FreezeByName (гэта IFixture мацаванне, радок назвы) {значэння пераменнага = fixture.Create (); fixture.Customizations.Add (новы ParameterNameSpecimenBuilder (імя, значэнне)); вяртаецца значэнне; }
дададзена аўтар Holstebroe, крыніца
Гэта ўзрушаюча! Дзякуючы.
дададзена аўтар Sam, крыніца
Гэта выдатна, дзякуй Нік. Я думаю, што адзінае пытанне, які я магу забраць, калі вы выкарыстоўваеце адзін і той жа асобнік арматуры, каб стварыць яшчэ адзін асобнік класа, які таксама мае параметр «імя» у канструктару тыпу радка. Шанцы, што адбываецца гэта тонкі, але, проста кажучы. Ці згодныя вы?
дададзена аўтар Dandré, крыніца
Так, я згодны, што гэта абмежаванне, але я згодны, гэта таксама не вельмі верагодна, выкліча ў вас якіх-небудзь рэальных праблем.
дададзена аўтар Nick Soper, крыніца
@Holstebroe +1 добрая ідэя.
дададзена аўтар Nick Soper, крыніца

Так што я ўпэўнены, што людзі маглі б выпрацаваць абагульненую рэалізацыю прапановы Маркі, але я думаў, што я адпраўлю яго для каментароў.

Я стварыў агульны ParameterNameSpecimenBuilder заснаваны на Марка LifeSpanArg :

public class ParameterNameSpecimenBuilder : ISpecimenBuilder
{
    private readonly string name;
    private readonly T value;

    public ParameterNameSpecimenBuilder(string name, T value)
    {
       //we don't want a null name but we might want a null value
        if (string.IsNullOrWhiteSpace(name))
        {
            throw new ArgumentNullException("name");
        }

        this.name = name;
        this.value = value;
    }

    public object Create(object request, ISpecimenContext context)
    {
        var pi = request as ParameterInfo;
        if (pi == null)
        {
            return new NoSpecimen(request);
        }

        if (pi.ParameterType != typeof(T) ||
            !string.Equals(
                pi.Name, 
                this.name, 
                StringComparison.CurrentCultureIgnoreCase))
        {
            return new NoSpecimen(request);
        }

        return this.value;
    }
}

Я тады вызначыў агульны FreezeByName <�код /> метад пашырэння IFixture , які ўсталёўвае налады:

public static class FreezeByNameExtension
{
    public static void FreezeByName(this IFixture fixture, string name, T value)
    {
        fixture.Customizations.Add(new ParameterNameSpecimenBuilder(name, value));
    }
}

Наступны тэст цяпер будзе праходзіць:

[TestMethod]
public void FreezeByName_Sets_Value1_And_Value2_Independently()
{
    //// Arrange
    IFixture arrangeFixture = new Fixture();

    string myValue1 = arrangeFixture.Create();
    string myValue2 = arrangeFixture.Create();

    IFixture sutFixture = new Fixture();
    sutFixture.FreezeByName("value1", myValue1);
    sutFixture.FreezeByName("value2", myValue2);

    //// Act
    TestClass result = sutFixture.Create>();

    //// Assert
    Assert.AreEqual(myValue1, result.Value1);
    Assert.AreEqual(myValue2, result.Value2);
}

public class TestClass
{
    public TestClass(T value1, T value2)
    {
        this.Value1 = value1;
        this.Value2 = value2;
    }

    public T Value1 { get; private set; }

    public T Value2 { get; private set; }
}
20
дададзена
Працаваў як шарм. Зробленая крыху гібрыдная перагрузка, калі замарожанае значэнне па-ранейшаму генеруюцца аўтаматычна: адкрытага статычны Т FreezeByName (гэта IFixture мацаванне, радок назвы) {значэння пераменнага = fixture.Create (); fixture.Customizations.Add (новы ParameterNameSpecimenBuilder (імя, значэнне)); вяртаецца значэнне; }
дададзена аўтар Holstebroe, крыніца
Гэта ўзрушаюча! Дзякуючы.
дададзена аўтар Sam, крыніца
Гэта выдатна, дзякуй Нік. Я думаю, што адзінае пытанне, які я магу забраць, калі вы выкарыстоўваеце адзін і той жа асобнік арматуры, каб стварыць яшчэ адзін асобнік класа, які таксама мае параметр «імя» у канструктару тыпу радка. Шанцы, што адбываецца гэта тонкі, але, проста кажучы. Ці згодныя вы?
дададзена аўтар Dandré, крыніца
Так, я згодны, што гэта абмежаванне, але я згодны, гэта таксама не вельмі верагодна, выкліча ў вас якіх-небудзь рэальных праблем.
дададзена аўтар Nick Soper, крыніца
@Holstebroe +1 добрая ідэя.
дададзена аўтар Nick Soper, крыніца

Вы павінны замяніць:

string knownValue = fixture.Freeze("My known value");

з:

fixture.Inject("My known value");

Вы можаце прачытаць больш пра Inject .

13
дададзена
FWIW, у той час як гэты адказ з'яўляецца добрае тлумачэнне таго, як Замацаваць і Inject працы, прапанаванае рашэнне прывядзе да <�я> усе радкі, каб атрымаць значэнне «My вядомае значэнне », якое не можа быць тое, што ОП хоча.
дададзена аўтар Mark Seemann, крыніца
@MarkSeemann напэўна ёсць законныя выпадкі, калі клас мае два розныя залежнасці радкоў (ці два залежнасцяў любога тыпу), і яны павінны быць усталяваны ў розныя значэнні для модульнага тэставання? Напрыклад, у мяне ёсць патрабаванне, каб вырабіць кошык пакупак <�я> стыль рашэнне (<�Ь> не фактычная кошык), якая мае два розных магчымасцяў: максімум X элементаў аднаго тыпу і максімальныя Y элементы іншага тыпу. Мне трэба, каб наладзіць іх, каб быць рознымі значэннямі ў адзінкавых тэстах так, а не выкарыстоўваць AutoFixture для гэтых тэстаў я выкарыстаў клас будаўніка.
дададзена аўтар Nick Soper, крыніца
@MarkSeemann - ёсць нейкі спосаб, што я магу выкарыстоўваць AutoFixture для налады розных вядомых значэнняў аднаго і таго ж тыпу ў канструктару?
дададзена аўтар Nick Soper, крыніца
Дзякуючы @NikosBaxevanis, што адказалі на маё пытанне зусім.
дададзена аўтар Nick Soper, крыніца

Вы павінны замяніць:

string knownValue = fixture.Freeze("My known value");

з:

fixture.Inject("My known value");

Вы можаце прачытаць больш пра Inject .

13
дададзена
FWIW, у той час як гэты адказ з'яўляецца добрае тлумачэнне таго, як Замацаваць і Inject працы, прапанаванае рашэнне прывядзе да <�я> усе радкі, каб атрымаць значэнне «My вядомае значэнне », якое не можа быць тое, што ОП хоча.
дададзена аўтар Mark Seemann, крыніца
@MarkSeemann напэўна ёсць законныя выпадкі, калі клас мае два розныя залежнасці радкоў (ці два залежнасцяў любога тыпу), і яны павінны быць усталяваны ў розныя значэнні для модульнага тэставання? Напрыклад, у мяне ёсць патрабаванне, каб вырабіць кошык пакупак <�я> стыль рашэнне (<�Ь> не фактычная кошык), якая мае два розных магчымасцяў: максімум X элементаў аднаго тыпу і максімальныя Y элементы іншага тыпу. Мне трэба, каб наладзіць іх, каб быць рознымі значэннямі ў адзінкавых тэстах так, а не выкарыстоўваць AutoFixture для гэтых тэстаў я выкарыстаў клас будаўніка.
дададзена аўтар Nick Soper, крыніца
@MarkSeemann - ёсць нейкі спосаб, што я магу выкарыстоўваць AutoFixture для налады розных вядомых значэнняў аднаго і таго ж тыпу ў канструктару?
дададзена аўтар Nick Soper, крыніца
Дзякуючы @NikosBaxevanis, што адказалі на маё пытанне зусім.
дададзена аўтар Nick Soper, крыніца

Вы можа зрабіць нешта накшталт гэтага. Уявіце сабе, што вы хочаце, каб прысвоіць пэўнае значэнне для TimeSpan Аргумент называецца працягласць жыцця .

public class LifespanArg : ISpecimenBuilder
{
    private readonly TimeSpan lifespan;

    public LifespanArg(TimeSpan lifespan)
    {
        this.lifespan = lifespan;
    }

    public object Create(object request, ISpecimenContext context)
    {
        var pi = request as ParameterInfo;
        if (pi == null)
            return new NoSpecimen(request);

        if (pi.ParameterType != typeof(TimeSpan) ||
            pi.Name != "lifespan")   
            return new NoSpecimen(request);

        return this.lifespan;
    }
}

Абавязкова трэба, але ён можа быць выкарыстаны, як гэта:

var fixture = new Fixture();
fixture.Customizations.Add(new LifespanArg(mySpecialLifespanValue));

var sut = fixture.Create();

Гэты падыход можна абагульніць да некаторай ступені, але ў рэшце рэшт, мы абмежаваныя адсутнасцю строга тыпізаваных чынам, каб атрымаць ParameterInfo ад канкрэтнага канструктара або аргументу метады.

10
дададзена
дзякуй за гэта. Я думаю, я дадам агульны метад пашырэння, заснаваны на гэтым для выкарыстання ў будучых выпрабаваннях.
дададзена аўтар Nick Soper, крыніца

Вы можа зрабіць нешта накшталт гэтага. Уявіце сабе, што вы хочаце, каб прысвоіць пэўнае значэнне для TimeSpan Аргумент называецца працягласць жыцця .

public class LifespanArg : ISpecimenBuilder
{
    private readonly TimeSpan lifespan;

    public LifespanArg(TimeSpan lifespan)
    {
        this.lifespan = lifespan;
    }

    public object Create(object request, ISpecimenContext context)
    {
        var pi = request as ParameterInfo;
        if (pi == null)
            return new NoSpecimen(request);

        if (pi.ParameterType != typeof(TimeSpan) ||
            pi.Name != "lifespan")   
            return new NoSpecimen(request);

        return this.lifespan;
    }
}

Абавязкова трэба, але ён можа быць выкарыстаны, як гэта:

var fixture = new Fixture();
fixture.Customizations.Add(new LifespanArg(mySpecialLifespanValue));

var sut = fixture.Create();

Гэты падыход можна абагульніць да некаторай ступені, але ў рэшце рэшт, мы абмежаваныя адсутнасцю строга тыпізаваных чынам, каб атрымаць ParameterInfo ад канкрэтнага канструктара або аргументу метады.

10
дададзена
дзякуй за гэта. Я думаю, я дадам агульны метад пашырэння, заснаваны на гэтым для выкарыстання ў будучых выпрабаваннях.
дададзена аўтар Nick Soper, крыніца

Я плата як @Nick была амаль там. Пры пераазначэнне канструктар аргумент, ён павінен быць для дадзенага тыпу і ён абмяжоўваецца толькі гэтым тыпам.

Спачатку мы ствараем новы ISpecimenBuilder, які глядзіць на «Member.DeclaringType», каб захаваць правільны маштаб.

public class ConstructorArgumentRelay : ISpecimenBuilder
{
    private readonly string _paramName;
    private readonly TValueType _value;

    public ConstructorArgumentRelay(string ParamName, TValueType value)
    {
        _paramName = ParamName;
        _value = value;
    }

    public object Create(object request, ISpecimenContext context)
    {
        if (context == null)
            throw new ArgumentNullException("context");
        ParameterInfo parameter = request as ParameterInfo;
        if (parameter == null)
            return (object)new NoSpecimen(request);
        if (parameter.Member.DeclaringType != typeof(TTarget) ||
            parameter.Member.MemberType != MemberTypes.Constructor ||
            parameter.ParameterType != typeof(TValueType) ||
            parameter.Name != _paramName)
            return (object)new NoSpecimen(request);
        return _value;
    }
}

Далей мы ствараем метад пашырэння, каб дазволіць нам лёгка падключыць яго з AutoFixture.

public static class AutoFixtureExtensions
{
    public static IFixture ConstructorArgumentFor(
        this IFixture fixture, 
        string paramName,
        TValueType value)
    {
        fixture.Customizations.Add(
           new ConstructorArgumentRelay(paramName, value)
        );
        return fixture;
    }
}

Цяпер мы створым два аналагічных класаў для тэставання с.

    public class TestClass
    {
        public TestClass(T value1, T value2)
        {
            Value1 = value1;
            Value2 = value2;
        }

        public T Value1 { get; private set; }
        public T Value2 { get; private set; }
    }

    public class SimilarClass
    {
        public SimilarClass(T value1, T value2)
        {
            Value1 = value1;
            Value2 = value2;
        }

        public T Value1 { get; private set; }
        public T Value2 { get; private set; }
    }

Нарэшце, мы правяраем яго з пашырэннем першапачатковага выпрабаванні, каб пераканацца, што ён не будзе перакрываць падобныя імёны і тыпізаваных аргументаў канструктара.

[TestFixture]
public class AutoFixtureTests
{
    [Test]
    public void Can_Create_Class_With_Specific_Parameter_Value()
    {
        string wanted = "This is the first string";
        string wanted2 = "This is the second string";
        Fixture fixture = new Fixture();
        fixture.ConstructorArgumentFor, string>("value1", wanted)
               .ConstructorArgumentFor, string>("value2", wanted2);

        TestClass t = fixture.Create>();
        SimilarClass s = fixture.Create>();

        Assert.AreEqual(wanted,t.Value1);
        Assert.AreEqual(wanted2,t.Value2);
        Assert.AreNotEqual(wanted,s.Value1);
        Assert.AreNotEqual(wanted2,s.Value2);
    }        
}
8
дададзена
Працавалі выдатна падыходзіць для мяне, дзякуй!
дададзена аўтар Lukie, крыніца

Я плата як @Nick была амаль там. Пры пераазначэнне канструктар аргумент, ён павінен быць для дадзенага тыпу і ён абмяжоўваецца толькі гэтым тыпам.

Спачатку мы ствараем новы ISpecimenBuilder, які глядзіць на «Member.DeclaringType», каб захаваць правільны маштаб.

public class ConstructorArgumentRelay : ISpecimenBuilder
{
    private readonly string _paramName;
    private readonly TValueType _value;

    public ConstructorArgumentRelay(string ParamName, TValueType value)
    {
        _paramName = ParamName;
        _value = value;
    }

    public object Create(object request, ISpecimenContext context)
    {
        if (context == null)
            throw new ArgumentNullException("context");
        ParameterInfo parameter = request as ParameterInfo;
        if (parameter == null)
            return (object)new NoSpecimen(request);
        if (parameter.Member.DeclaringType != typeof(TTarget) ||
            parameter.Member.MemberType != MemberTypes.Constructor ||
            parameter.ParameterType != typeof(TValueType) ||
            parameter.Name != _paramName)
            return (object)new NoSpecimen(request);
        return _value;
    }
}

Далей мы ствараем метад пашырэння, каб дазволіць нам лёгка падключыць яго з AutoFixture.

public static class AutoFixtureExtensions
{
    public static IFixture ConstructorArgumentFor(
        this IFixture fixture, 
        string paramName,
        TValueType value)
    {
        fixture.Customizations.Add(
           new ConstructorArgumentRelay(paramName, value)
        );
        return fixture;
    }
}

Цяпер мы створым два аналагічных класаў для тэставання с.

    public class TestClass
    {
        public TestClass(T value1, T value2)
        {
            Value1 = value1;
            Value2 = value2;
        }

        public T Value1 { get; private set; }
        public T Value2 { get; private set; }
    }

    public class SimilarClass
    {
        public SimilarClass(T value1, T value2)
        {
            Value1 = value1;
            Value2 = value2;
        }

        public T Value1 { get; private set; }
        public T Value2 { get; private set; }
    }

Нарэшце, мы правяраем яго з пашырэннем першапачатковага выпрабаванні, каб пераканацца, што ён не будзе перакрываць падобныя імёны і тыпізаваных аргументаў канструктара.

[TestFixture]
public class AutoFixtureTests
{
    [Test]
    public void Can_Create_Class_With_Specific_Parameter_Value()
    {
        string wanted = "This is the first string";
        string wanted2 = "This is the second string";
        Fixture fixture = new Fixture();
        fixture.ConstructorArgumentFor, string>("value1", wanted)
               .ConstructorArgumentFor, string>("value2", wanted2);

        TestClass t = fixture.Create>();
        SimilarClass s = fixture.Create>();

        Assert.AreEqual(wanted,t.Value1);
        Assert.AreEqual(wanted2,t.Value2);
        Assert.AreNotEqual(wanted,s.Value1);
        Assert.AreNotEqual(wanted2,s.Value2);
    }        
}
8
дададзена
Працавалі выдатна падыходзіць для мяне, дзякуй!
дададзена аўтар Lukie, крыніца

Гэта, як уяўляецца, найбольш поўны набор рашэнняў тут. Так што я збіраюся дадаць маё:

Першае, што трэба стварыць ISpecimenBuilder , які можа апрацоўваць некалькі параметраў канструктара

internal sealed class CustomConstructorBuilder : ISpecimenBuilder
{
    private readonly Dictionary _ctorParameters = new Dictionary();

    public object Create(object request, ISpecimenContext context)
    {
        var type = typeof (T);
        var sr = request as SeededRequest;
        if (sr == null || !sr.Request.Equals(type))
        {
            return new NoSpecimen(request);
        }

        var ctor = type.GetConstructors(BindingFlags.Instance | BindingFlags.Public).FirstOrDefault();
        if (ctor == null)
        {
            return new NoSpecimen(request);
        }

        var values = new List();
        foreach (var parameter in ctor.GetParameters())
        {
            if (_ctorParameters.ContainsKey(parameter.Name))
            {
                values.Add(_ctorParameters[parameter.Name]);
            }
            else
            {
                values.Add(context.Resolve(parameter.ParameterType));
            }
        }

        return ctor.Invoke(BindingFlags.CreateInstance, null, values.ToArray(), CultureInfo.InvariantCulture);
    }

    public void Addparameter(string paramName, object val)
    {
        _ctorParameters.Add(paramName, val);
    }
 }

Затым стварыць метад пашырэння, што спрашчае выкарыстанне створанага забудоўшчыка

   public static class AutoFixtureExtensions
    {
        public static void FreezeActivator(this IFixture fixture, object parameters)
        {
            var builder = new CustomConstructorBuilder();
            foreach (var prop in parameters.GetType().GetProperties())
            {
                builder.Addparameter(prop.Name, prop.GetValue(parameters));
            }

            fixture.Customize(x => builder);
        }
    }

І выкарыстанне:

var f = new Fixture();
f.FreezeActivator(new { privateId = 15, parentId = (long?)33 });
5
дададзена
Прыемны і чысты раствор. У нашых праектах мы часам некалькі залежнасцяў для таго ж тыпу, і мы хочам, каб праверыць адно з то, гэты параметр з імем вельмі добра падыходзіць для нашых патрэб. Дзякуй за абмен.
дададзена аўтар Diego Mendes, крыніца

Гэта, як уяўляецца, найбольш поўны набор рашэнняў тут. Так што я збіраюся дадаць маё:

Першае, што трэба стварыць ISpecimenBuilder , які можа апрацоўваць некалькі параметраў канструктара

internal sealed class CustomConstructorBuilder : ISpecimenBuilder
{
    private readonly Dictionary _ctorParameters = new Dictionary();

    public object Create(object request, ISpecimenContext context)
    {
        var type = typeof (T);
        var sr = request as SeededRequest;
        if (sr == null || !sr.Request.Equals(type))
        {
            return new NoSpecimen(request);
        }

        var ctor = type.GetConstructors(BindingFlags.Instance | BindingFlags.Public).FirstOrDefault();
        if (ctor == null)
        {
            return new NoSpecimen(request);
        }

        var values = new List();
        foreach (var parameter in ctor.GetParameters())
        {
            if (_ctorParameters.ContainsKey(parameter.Name))
            {
                values.Add(_ctorParameters[parameter.Name]);
            }
            else
            {
                values.Add(context.Resolve(parameter.ParameterType));
            }
        }

        return ctor.Invoke(BindingFlags.CreateInstance, null, values.ToArray(), CultureInfo.InvariantCulture);
    }

    public void Addparameter(string paramName, object val)
    {
        _ctorParameters.Add(paramName, val);
    }
 }

Затым стварыць метад пашырэння, што спрашчае выкарыстанне створанага забудоўшчыка

   public static class AutoFixtureExtensions
    {
        public static void FreezeActivator(this IFixture fixture, object parameters)
        {
            var builder = new CustomConstructorBuilder();
            foreach (var prop in parameters.GetType().GetProperties())
            {
                builder.Addparameter(prop.Name, prop.GetValue(parameters));
            }

            fixture.Customize(x => builder);
        }
    }

І выкарыстанне:

var f = new Fixture();
f.FreezeActivator(new { privateId = 15, parentId = (long?)33 });
5
дададзена
Прыемны і чысты раствор. У нашых праектах мы часам некалькі залежнасцяў для таго ж тыпу, і мы хочам, каб праверыць адно з то, гэты параметр з імем вельмі добра падыходзіць для нашых патрэб. Дзякуй за абмен.
дададзена аўтар Diego Mendes, крыніца