سؤال

أحاول استخدام مصنع سوت "نمط" لإنشاء سوت بلدي.

بالنظر إلى بنية SUT:

namespace MySut
{
    public class Dep1
    {
    }

    public class Dep2
    {
    }

    public class Sut
    {
        public Sut( Dep1 dep1, Dep2 dep2 )
        {
        }
    }
}

أنا استخدم Autofixture, ، وأتساءل ما هي أفضل طريقة لانهيار المواصفات التالية وطريقة مصنع SUT المرتبطة بها [قيمة ولكن] مشغول:

namespace MySpecifications
{
    using MySut;
    public class MySpecification
    {
        public void TestCore()
        {
            // Dont care about dependencies, testing core functionality
            var sut = CreateSut();
        }

        public void TestDep1Interaction()
        {
            // Dont care about Dep2, want to observe stuff on the Dep1 dependent object
            var sut = CreateSut( new Mock<Dep1>().Object );
        }

        public void TestDep2Interaction()
        {
            // Dont care about Dep1, want to observe stuff on the Dep2 dependent object
            var sut = CreateSut( new Mock<Dep2>().Object );
        }

        private object CreateSut( )
        {
            return CreateSut( CreateDep1(), CreateDep2() );
        }

        private object CreateSut( Dep1 dep1 )
        {
            return CreateSut( dep1, CreateDep2() );
        }

        private object CreateSut( Dep2 dep2 )
        {
            return CreateSut( CreateDep1(), dep2 );
        }

        private Sut CreateSut( Dep1 dep1, Dep2 dep2 )
        {
            return new Sut( dep1, dep2 );
        }

        private static Dep1 CreateDep1()
        {
            return new Fixture().CreateAnonymous<Dep1>();
        }

        private static Dep2 CreateDep2()
        {
            return new Fixture().CreateAnonymous<Dep2>();
        }
    }
}

إلى شيء مثل:

    public class MyAutoFixturedSpecification
    {
        public void TestCore()
        {
            // Dont care about dependencies, testing core functionality
            var sut = CreateSut();
        }

        public void TestDep1Interaction()
        {
            // Dont care about Dep2, want to observe stuff on the Dep1 dependent object
            var sut = CreateSut( new Mock<Dep1>().Object );
        }

        public void TestDep2Interaction()
        {
            // Dont care about Dep1, want to observe stuff on the Dep2 dependent object
            var sut = CreateSut( new Mock<Dep2>().Object );
        }

        private object CreateSut( params object[] injectedNonAnonymouses )
        {
            return new Fixture(  ).Build<Sut>(  )./*??????*/;
        }
    }

أو:

    public class MyAnticipatedAutoFixturedSpecification
    {
        public void TestCore()
        {
            // Dont care about dependencies, testing core functionality
            var sut = new Fixture(  ).Build<Sut>().CreateAnonymous(  );
        }

        public void TestDep1Interaction()
        {
            // Dont care about Dep2, want to observe stuff on the Dep1 dependent object
            var sut = new Fixture().Build<Sut>()/*.With( new Mock<Dep1>().Object )*/.CreateAnonymous();
        }

        public void TestDep2Interaction()
        {
            // Dont care about Dep1, want to observe stuff on the Dep2 dependent object
            var sut = new Fixture().Build<Sut>()/*.With( new Mock<Dep2>().Object )*/.CreateAnonymous();
        }
    }

أي ، إزالة جميع خردة المصنع ، بحيث يمكن لمواصفاتي التعامل بسهولة مع الانتقال إلى:

namespace MySutWithNewDependency
{
    public class Dep1
    {
    }

    public class Dep2
    {
    }

    public class Dep3
    {
    }

    public class Sut
    {
        public Sut( Dep1 dep1, Dep2 dep2, Dep3 dep3 )
        {
        }
    }
}

في حين أن هناك تداخلًا مع فكرة حاوية تلقائية ، إلا أنني ما زلت لا أبحث عن مطرقة ذهبية - مجرد وسيلة للتمكن لإعادة النظر في المكالمات الصريحة إلى مُنشئ SUT كلما تغيرت مجموعة التبعيات.

(أيضًا باستخدام Xunit.net (نمط Subspec) ، MOQ ، Ninject2 (على الرغم من أنه لا تريد استخدام DI في مواصفاتي))

هل كانت مفيدة؟

المحلول

Autofixture يمكن أن تفعل كل هذا من أجلك دون كل هذا السقالات الإضافية. سأبدأ بمبنى أساسي وأستخدمه لحلها Sut:

var fixture = new Fixture();
var sut = fixture.CreateAnonymous<Sut>();

هذا يفترض ذلك Dep1 و Dep2 يمكن إنشاؤها تلقائيًا عن طريق AutoFixture ، ولكن كما هو معروض ، يمكنهم لأن لديهم منشئات افتراضية.

عندما تريد تجاوز نوع معين ، يمكنك استخدام طريقة التسجيل مثل هذا:

var fixture = new Fixture();

var mock = new Mock<Dep1>();
fixture.Register(mock.Object);
// Setup mock if necessary...

var sut = fixture.CreateAnonymous<Sut>();

هذا سوف يسبب fixture ليستخدم mock.Object في أي وقت Dep1 مطلوب ، بما في ذلك عندما Sut تم الاحتجاج بمنشئ. هذا بالتأكيد يناسب متطلباتك من أن تكون قويًا في مواجهة المنشئين المتطورين ، وقد تم بناء أحد الأسباب الرئيسية للسيارات التلقائية على هذا النحو.

في سيناريو أكثر واقعية ، قد يكون DEP1 و DEP2 واجهات ، وفي هذه الحالة قد ترغب في تسجيلها كجزء من "التثبيت الافتراضي". هذا بالتأكيد ربما بسبب ذلك آخر مكالمة لتسجيل الفوز. هذا يعني أنه يمكنك تكوين مثيل التثبيت مع بعض الإعدادات الافتراضية الجيدة ولا تزال قادرة على تجاوز أنواع محددة كلما احتجت إلى القيام بذلك.

أنا شخصياً أستخدم AutoFixture كحاوية التخلص التلقائي. هذه المناقشة يوفر تلميحًا إلى كيفية إمكانية ذلك مع AutoFixture 1.1 API ، لكن kernel الجديدة لـ AutoFixture 2.0 ستوفر خيارات تمديد أفضل بكثير. عندما أصل إليها ، سأكتب منشورًا للمدونة حول هذا الموضوع.

ملاحظة: آسف على الرد المتأخر ، لكنني لم أر سؤالك من قبل الآن. في المستقبل ، لا تتردد في Ping Me (على سبيل المثال على Twitter) للحصول على رد أسرع.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top