هل السخرية قادرة على استبدال الوظائف ملفوفة داخل طريقة؟

StackOverflow https://stackoverflow.com/questions/1368414

  •  21-09-2019
  •  | 
  •  

سؤال

أحاول تحديد طريقة لمحاكاة حالة حول الوصول إلى قاعدة بيانات دون الوصول ... ربما يبدو هذا مجنونًا تمامًا ، لكنه ليس كذلك.

فيما يلي مثال على طريقة أود اختبارها:

    public IDevice GetDeviceFromRepository(string name)
    {
        IDevice device = null;
        IDbConnection connection = new SqlConnection(ConnectionString);
        connection.Open();
        try
        {
            IDbCommand command = connection.CreateCommand();
            command.CommandText = string.Format("SELECT DEVICE_ID,DEVICE_NAME FROM DEVICE WHERE DEVICE_NAME='{0}'", name);
            IDataReader dataReader = command.ExecuteReader();
            if(dataReader.NextResult())
            {
                device = new Device(dataReader.GetInt32(0),dataReader.GetString(1));
            }
        }
        finally
        {
            connection.Close();
        }
        return device;
    }

أنا أتظاهر بالسخرية من Idatareader حتى أتمكن من التحكم في ما يتم قراءته. شيء من هذا القبيل (باستخدام إطار عمل MOQ):

    [TestMethod()]
    public void GetDeviceFromRepositoryTest()
    {
        Mock<IDataReader> dataReaderMock = new Mock<IDataReader>();
        dataReaderMock.Setup(x => x.NextResult()).Returns(true);
        dataReaderMock.Setup(x => x.GetInt32(0)).Returns(000);
        dataReaderMock.Setup(x => x.GetString(1)).Returns("myName");
        Mock<IDbCommand> commandMock = new Mock<IDbCommand>();
        commandMock.Setup(x => x.ExecuteReader()).Returns(dataReaderMock.Object);
        Mock<RemoveDeviceManager> removeMock = new Mock<RemoveDeviceManager>();
        removeMock.Setup()
        RemoveDeviceManager target =new RemoveDeviceManager(new Device(000, "myName")); 
        string name = string.Empty; 
        IDevice expected = new Device(000, "myName"); // TODO: Initialize to an appropriate value
        IDevice actual;
        actual = target.GetDeviceFromRepository(name);
        Assert.AreEqual(expected.SerialNumber, actual.SerialNumber);
        Assert.AreEqual(expected.Name, actual.Name);
    }

سؤالي هو ما إذا كان بإمكاني فرض طريقة getDeviceFromRepository لاستبدال idatareader من قبل واحد.

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

المحلول

على الرغم من أنك تستخدم حاليًا MOQ ، أعتقد أنه لا يمكن تحقيق الوظيفة التي تبحث عنها دون حقن التبعية إلا إذا كنت تستخدم عزل Typemock (إخلاء المسئولية - عملت في Typemock).

يحتوي المعزل على ميزة تسمى "الكائنات المستقبلية" التي تمكن من استبدال مثيل مستقبلي لكائن بكائن مزيف تم إنشاؤه مسبقًا:

 // Create fake (stub/mock whateever) objects
 var fakeSqlConnection = Isolate.Fake.Instance<SqlConnection>();
 var fakeCommand = Isolate.Fake.Instance<SqlCommand>();
 Isolate.WhenCalled(() => fakeSqlConnection.CreateCommand()).WillReturn(fakeCommand);

 var fakeReader = Isolate.Fake.Instance<SqlDataReader>();
 Isolate.WhenCalled(() => fakeCommand.ExecuteReader()).WillReturn(fakeReader);

 // Next time SQLConnection is instantiated replace with our fake
 Isolate.Swap.NextInstance<SqlConnection>().With(fakeSqlConnection);

نصائح أخرى

أعتقد أن المشكلة هنا هي اعتمادك المباشر في النهاية SQLConnection. إذا كنت تستخدم بعض البديل في حقن التبعية بحيث يحصل الكود الخاص بك على إمكانية الوصول إلى IDBCommand دون معرفة كيفية بناءه ، فستتمكن من ضخ نفخك دون الكثير من المتاعب.

أنا أفهم أن هذا لا يجيب تمامًا على سؤالك ، ولكن على المدى الطويل ، فإن القيام بالأشياء كما هو موضح سيمنحك قابلية اختبار أفضل بكثير.

وأنا أتفق مع إجابة فرانك على أن التحرك نحو حقن التبعية هو الحل الأفضل على المدى الطويل ، ولكن هناك بعض الخطوات الوسيطة التي يمكنك اتخاذها لتحريكك في هذا الاتجاه دون أن يعقد كل شيء.

شيء واحد هو نقل بناء فئة IDBConnection إلى طريقة افتراضية محمية داخل فئتك:

protected virtual IDbConnection CreateConnection()
{
    return new SqlConnection(ConnectionString);
}

بعد ذلك ، يمكنك إنشاء إصدار اختبار من فصلك مثل SO:

public class TestingRemoteDeviceManager : RemoteDeviceManager
{
   public override IDbConnection CreateConnection()
   {
         IDbConnection conn = new Mock<IDbConnection>();
         //mock out the rest of the interface, as well as the IDbCommand and
         //IDataReader interfaces
         return conn;
   }
}

هذا يعيد HOM أو IDBConnection وهمية بدلاً من SQLConnection الخرسانية. يمكن لهذا المزيف بعد ذلك إرجاع كائن IDBCommand مزيف ، والذي يمكنه بعد ذلك إعادة كائن Idatareader مزيف.

تعويذة اختبار حتى يتحول الخوف بالملل. أعتقد أنك عبرت هذا الخط هنا. إذا كنت ستتحكم في قارئ البيانات ، فإن الكود الوحيد الذي تختبره هو:

device = new Device(dataReader.GetInt32(0),dataReader.GetString(1));

لا يوجد شيء تقريبًا للاختبار هنا ، وهو أمر جيد: يجب أن تكون طبقة البيانات بسيطة وغبية. لذلك لا تحاول وحدة اختبار طبقة البيانات الخاصة بك. إذا شعرت أنه يجب عليك اختباره ، فاختبره تكامله مقابل قاعدة بيانات حقيقية.

بالطبع ، إخفاء طبقة البيانات الخاصة بك خلف أ IDeviceRepository واجهة بحيث يمكنك بسهولة السخرية من ذلك من أجل الاختبار آخر الكود لا يزال فكرة جيدة.

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