multithreading - كيف يمكنني وحدة اختبار رمز مترابطة؟




unit-testing (17)

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

هذا يبدو وكأنه مشكلة رئيسية للمبرمجين اليوم ، سيكون من المفيد تجميع معرفتنا حول هذا imho واحد.


(إن أمكن) لا تستخدم مؤشرات الترابط ، استخدم الممثلين / الكائنات النشطة. سهل الاختبار.


أتعامل مع اختبارات الوحدة للمكونات الملولبة بنفس الطريقة التي أتعامل بها مع أي اختبار وحدة ، وهذا هو عكس أطر التحكم والعزلة. أتطور في .Net - الساحة وخارج الصندوق الخيط (من بين أمور أخرى) صعب جدا (أقول مستحيل تقريبا) لعزل كامل.

لذلك كتبت أغلفة تبدو وكأنها شيء من هذا القبيل (مبسطة):

public interface IThread
{
    void Start();
    ...
}

public class ThreadWrapper : IThread
{
    private readonly Thread _thread;

    public ThreadWrapper(ThreadStart threadStart)
    {
        _thread = new Thread(threadStart);
    }

    public Start()
    {
        _thread.Start();
    }
}

public interface IThreadingManager
{
    IThread CreateThread(ThreadStart threadStart);
}

public class ThreadingManager : IThreadingManager
{
    public IThread CreateThread(ThreadStart threadStart)
    {
         return new ThreadWrapper(threadStart)
    }
}

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

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


إذا كنت تختبر مؤشر ترابط بسيط جديد (runnable) .run () من يمكنك خيط مؤشر ترابط لتشغيل runnable بالتعاقب

على سبيل المثال ، إذا استدعى رمز الكائن الذي تم اختباره مؤشر ترابط جديد مثل هذا

Class TestedClass{
    public void doAsychOp(){
       new Thread(new myRunnable()).start()
    }

من يسخر من المواضيع الجديدة وتشغيل حجة runnable يمكن أن يساعد على التوالي

@Mock
private Thread threadMock;

@Test
public void myTest() throws Exception {
    PowerMockito.mockStatic(Thread.class);
    //when new thread is created execute runnable immediately 
    PowerMockito.whenNew(Thread.class).withAnyArguments().then(new Answer<Thread>() {
        @Override
        public Thread answer(InvocationOnMock invocation) throws Throwable {
                // immediately run the runnable
                Runnable runnable = invocation.getArgumentAt(0, Runnable.class);
                if(runnable != null) {
                    runnable.run();
                }
                return threadMock;//return a mock so Thread.start() will do nothing         
            }
        }); 
       TestedClass testcls = new TestedClass()
       testcls.doAsychOp(); //will invoke myRunnable.run in current thread
      //.... check expected 

إلقاء نظرة على إجابتي ذات الصلة في

تصميم فئة اختبار لحاجز مخصص

إنه منحاز نحو Java ولكنه يحتوي على ملخص معقول للخيارات.

باختصار على الرغم من (IMO) ليس استخدام بعض إطار الهوى التي من شأنها ضمان صحة ولكن كيف تذهب نحو تصميم لك رمز multithreaded. تقسيم المخاوف (التزامن والوظائف) يذهب بطريقة هائلة نحو زيادة الثقة. يشرح برنامج Objecting Orientated Software الذي يسترشد بالاختبارات بعض الخيارات بشكل أفضل مما يمكنني.

يعتبر التحليل الثابت والأساليب الرسمية (انظر : التزامن: نماذج الدولة وبرامج جافا ) خيارًا ، ولكنني وجدته محدودًا في التطوير التجاري.

لا ننسى أن اختبارات نمط التحميل / الحمولة نادرًا ما تكون مضمونة لإبراز المشاكل.

حظا طيبا وفقك الله!


انظر ، ليس هناك طريقة سهلة للقيام بذلك. أنا أعمل على مشروع أصلاً متعدد مؤشرات الترابط. تأتي الأحداث من نظام التشغيل ولا بد لي من معالجتها بشكل متزامن.

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

هناك العديد من الطرق لبرنامج multithreading التي تتجنب مؤشرات الترابط قيد التشغيل من خلال مثيلات في نفس الوقت. أبسط هو جعل جميع الأشياء الخاصة بك غير قابل للتغيير. بالطبع ، هذا غير ممكن عادة. لذلك عليك تحديد تلك الأماكن في التصميم الخاص بك حيث تتداخل المواضيع مع نفس المثيل وتقليل عدد تلك الأماكن. من خلال القيام بذلك ، يمكنك عزل بعض الفئات حيث يحدث multithreading بالفعل ، مما يقلل من التعقيد الكلي لاختبار النظام الخاص بك.

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

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

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


بالنسبة إلى Java ، راجع الفصل 12 من JCIP . هناك بعض الأمثلة الملموسة لكتابة اختبارات وحدة محددة ومتعددة الخيوط لاختبار ما لا يقل عن صحة ومثيلات الشفرة المتزامنة.

"إثبات" سلامة الصفحات مع اختبار وحدة هو أكثر دهاء. أعتقد أن هذا أفضل من خلال اختبار التكامل التلقائي على مجموعة متنوعة من الأنظمة الأساسية / التكوينات.


في Java: تحتوي الحزمة java.util.concurrent على بعض الفئات غير المعروفة ، والتي قد تساعد في كتابة JUnit-Tests.

القي نظرة على


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

  1. حدث أثر / اعادتها. يتطلب ذلك مراقبة الحدث ثم مراجعة الأحداث التي تم إرسالها. في إطار عمل UT ، يتضمن ذلك إرسال الأحداث يدويًا كجزء من اختبار ، ثم إجراء مراجعات بعد الوفاة.
  2. النصية. هذا هو المكان الذي تتفاعل فيه مع كود التشغيل مع مجموعة من المشغلات. "في x> foo ، baz ()". يمكن تفسير ذلك في إطار عمل UT حيث يوجد لديك نظام وقت التشغيل الذي يؤدي إلى اختبار معين على شرط معين.
  3. التفاعلية. من الواضح أن هذا لن يعمل في وضع الاختبار التلقائي. ؛)

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

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

حظا سعيدا ، ومواصلة العمل على المشكلة.


لديك كتاب في كتاب Clean Code الفصل 13 يوجد مقطع كامل مخصص هناك لاختبار التعليمات البرمجية ذات مؤشرات ترابط متعددة بالإضافة إلى التزامن بشكل عام والذي قد يساعدك في تصميم رمز أفضل ذو مؤشرات ترابط متعددة.


لرمز J2E ، لقد استخدمت SilkPerformer و LoadRunner و JMeter لاختبار التزامن للخيوط. كلهم يفعلون نفس الشيء. في الأساس ، يعطونك واجهة بسيطة نسبيًا لإدارة إصدارهم من الخادم الوكيل ، المطلوب ، لتحليل تدفق بيانات TCP / IP ، ومحاكاة العديد من المستخدمين الذين يقومون بطلبات متزامنة لخادم التطبيقات الخاص بك. يمكن أن يمنحك الخادم الوكيل القدرة على القيام بأشياء مثل تحليل الطلبات المقدمة ، من خلال تقديم الصفحة بأكملها وعنوان URL المرسل إلى الخادم ، بالإضافة إلى الاستجابة من الخادم ، بعد معالجة الطلب.

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

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

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

كلمة تحذير: يستغرق الأمر بعض الوقت لفهم هذه الأدوات. لن يكون الأمر مجرد تثبيت البرنامج وإطلاق واجهة المستخدم الرسومية (GUI) ، ما لم تكن قد تعرضت بالفعل لبعض البرامج متعددة المسارات. لقد حاولت تحديد ثلاث فئات أساسية من المجالات لفهمها (النماذج وبيانات الجلسة وملف تعريف الارتباط) ، على أمل أن يساعدك البدء في فهم هذه الموضوعات على الأقل في التركيز على النتائج السريعة ، بدلاً من الاضطرار إلى قراءة وثائق كاملة.


لقد اكتشفت مؤخراً (لـ Java) أداة تسمى Threadafe. إنها أداة تحليل ثابتة تشبه إلى حد كبير findbugs ولكن تحديدًا لمسائل متعددة مؤشرات الترابط. إنه ليس بديلاً للاختبار ولكن يمكنني أن أوصي به كجزء من كتابة Java متعددة مؤشرات الترابط الموثوقة.

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

إذا قمت بكتابة متعددة اللغات جاوة contemplateltd.com/threadsafe


لقد فعلت الكثير من هذا ، ونعم تمتص.

بعض النصائح:

  • GroboUtils لتشغيل مؤشرات ترابط اختبار متعددة
  • alphaWorks ConTest إلى صك أداة لتسبب interleavings تختلف بين التكرارات
  • قم بإنشاء حقل tearDown والتحقق منه في tearDown (راجع القائمة 1). إذا صادفت استثناء سيئًا في سلسلة محادثات أخرى ، فكل ما عليك هو تعيينه على أنه قابل للإزالة.
  • لقد أنشأت فئة utils في قائمة 2 ووجدت أنها لا تقدر بثمن ، خاصة waitForVerify و waitForCondition ، الأمر الذي سيزيد بشكل كبير من أداء اختباراتك.
  • استفد من AtomicBoolean في اختباراتك. هذا الخيط آمن ، وستحتاج غالبًا إلى نوع مرجع نهائي لتخزين القيم من فصول الاسترجاع وما شابه ذلك. انظر المثال في قائمة 3.
  • تأكد دائمًا من منح الاختبار مهلة (على سبيل المثال ، @Test(timeout=60*1000) ) ، حيث يمكن أحيانًا تعليق الاختبارات التزامن إلى الأبد عند كسرها

قائمة 1:

@After
public void tearDown() {
    if ( throwable != null )
        throw throwable;
}

قائمة 2:

import static org.junit.Assert.fail;
import java.io.File;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.Random;
import org.apache.commons.collections.Closure;
import org.apache.commons.collections.Predicate;
import org.apache.commons.lang.time.StopWatch;
import org.easymock.EasyMock;
import org.easymock.classextension.internal.ClassExtensionHelper;
import static org.easymock.classextension.EasyMock.*;

import ca.digitalrapids.io.DRFileUtils;

/**
 * Various utilities for testing
 */
public abstract class DRTestUtils
{
    static private Random random = new Random();

/** Calls {@link #waitForCondition(Integer, Integer, Predicate, String)} with
 * default max wait and check period values.
 */
static public void waitForCondition(Predicate predicate, String errorMessage) 
    throws Throwable
{
    waitForCondition(null, null, predicate, errorMessage);
}

/** Blocks until a condition is true, throwing an {@link AssertionError} if
 * it does not become true during a given max time.
 * @param maxWait_ms max time to wait for true condition. Optional; defaults
 * to 30 * 1000 ms (30 seconds).
 * @param checkPeriod_ms period at which to try the condition. Optional; defaults
 * to 100 ms.
 * @param predicate the condition
 * @param errorMessage message use in the {@link AssertionError}
 * @throws Throwable on {@link AssertionError} or any other exception/error
 */
static public void waitForCondition(Integer maxWait_ms, Integer checkPeriod_ms, 
    Predicate predicate, String errorMessage) throws Throwable 
{
    waitForCondition(maxWait_ms, checkPeriod_ms, predicate, new Closure() {
        public void execute(Object errorMessage)
        {
            fail((String)errorMessage);
        }
    }, errorMessage);
}

/** Blocks until a condition is true, running a closure if
 * it does not become true during a given max time.
 * @param maxWait_ms max time to wait for true condition. Optional; defaults
 * to 30 * 1000 ms (30 seconds).
 * @param checkPeriod_ms period at which to try the condition. Optional; defaults
 * to 100 ms.
 * @param predicate the condition
 * @param closure closure to run
 * @param argument argument for closure
 * @throws Throwable on {@link AssertionError} or any other exception/error
 */
static public void waitForCondition(Integer maxWait_ms, Integer checkPeriod_ms, 
    Predicate predicate, Closure closure, Object argument) throws Throwable 
{
    if ( maxWait_ms == null )
        maxWait_ms = 30 * 1000;
    if ( checkPeriod_ms == null )
        checkPeriod_ms = 100;
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    while ( !predicate.evaluate(null) ) {
        Thread.sleep(checkPeriod_ms);
        if ( stopWatch.getTime() > maxWait_ms ) {
            closure.execute(argument);
        }
    }
}

/** Calls {@link #waitForVerify(Integer, Object)} with <code>null</code>
 * for {@code maxWait_ms}
 */
static public void waitForVerify(Object easyMockProxy)
    throws Throwable
{
    waitForVerify(null, easyMockProxy);
}

/** Repeatedly calls {@link EasyMock#verify(Object[])} until it succeeds, or a
 * max wait time has elapsed.
 * @param maxWait_ms Max wait time. <code>null</code> defaults to 30s.
 * @param easyMockProxy Proxy to call verify on
 * @throws Throwable
 */
static public void waitForVerify(Integer maxWait_ms, Object easyMockProxy)
    throws Throwable
{
    if ( maxWait_ms == null )
        maxWait_ms = 30 * 1000;
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    for(;;) {
        try
        {
            verify(easyMockProxy);
            break;
        }
        catch (AssertionError e)
        {
            if ( stopWatch.getTime() > maxWait_ms )
                throw e;
            Thread.sleep(100);
        }
    }
}

/** Returns a path to a directory in the temp dir with the name of the given
 * class. This is useful for temporary test files.
 * @param aClass test class for which to create dir
 * @return the path
 */
static public String getTestDirPathForTestClass(Object object) 
{

    String filename = object instanceof Class ? 
        ((Class)object).getName() :
        object.getClass().getName();
    return DRFileUtils.getTempDir() + File.separator + 
        filename;
}

static public byte[] createRandomByteArray(int bytesLength)
{
    byte[] sourceBytes = new byte[bytesLength];
    random.nextBytes(sourceBytes);
    return sourceBytes;
}

/** Returns <code>true</code> if the given object is an EasyMock mock object 
 */
static public boolean isEasyMockMock(Object object) {
    try {
        InvocationHandler invocationHandler = Proxy
                .getInvocationHandler(object);
        return invocationHandler.getClass().getName().contains("easymock");
    } catch (IllegalArgumentException e) {
        return false;
    }
}
}

قائمة 3:

@Test
public void testSomething() {
    final AtomicBoolean called = new AtomicBoolean(false);
    subject.setCallback(new SomeCallback() {
        public void callback(Object arg) {
            // check arg here
            called.set(true);
        }
    });
    subject.run();
    assertTrue(called.get());
}

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

كتابة رمز متعدد مؤشرات للاختبار

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

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

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

اختبارات وحدة الكتابة لرمز متعددة الخيوط

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

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

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

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


هناك طريقة أخرى لاختبار كود الخيوط (kinda) ، والنظم المعقدة بشكل عام هي من خلال اختبار الزغب . إنها ليست رائعة ، ولن تجد كل شيء ، ولكن من المحتمل أن تكون مفيدة وبسيطة للقيام بها.

اقتبس:

اختبار الزغب أو الغموض هو تقنية اختبار البرامج التي توفر بيانات عشوائية ("زغب") لمدخلات البرنامج. إذا فشل البرنامج (على سبيل المثال ، من خلال التعطل ، أو عن طريق التأكيدات البرمجية المضمنة) ، يمكن ملاحظة العيوب. الميزة الرائعة لاختبار الزغب هي أن تصميم الاختبار بسيط للغاية ، وخالٍ من الأفكار المسبقة حول سلوك النظام.

...

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

...

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


واحد صعب حقا! في اختبارات الوحدة (C ++) ، قمت بتقسيم ذلك إلى عدة فئات على غرار نمط التزامن المستخدم:

  1. اختبارات الوحدة للفئات التي تعمل في موضوع واحد ولا تدرك الخيط - من السهل ، واختبار كالمعتاد.

  2. اختبارات الوحدة لكائنات المراقبة (تلك التي تنفذ أساليب متزامنة في مؤشر ترابط المتحكمين في المتصل) والتي تعرض واجهة برمجة تطبيقات عامة متزامنة - تقوم بإنشاء عدة سلاسل عمليات وهمية تمارس واجهة برمجة التطبيقات. بناء السيناريوهات التي تمارس الظروف الداخلية للكائن السلبي. قم بتضمين اختبار تشغيل أطول يدق بشكل أساسي من هيك من خيوط متعددة لفترة طويلة من الزمن. هذا غير علمي أعرفه ولكنه يبني الثقة.

  3. اختبارات الوحدة للأجسام النشطة (تلك التي تغلف خيطها أو سلاسل التحكم الخاصة بها) - تشبه # 2 أعلاه مع اختلافات تعتمد على تصميم الفئة. قد تكون واجهة برمجة التطبيقات العامة للحظر أو عدم المنع ، وقد يحصل المتصلون على عقود مستقبلية ، وقد تصل البيانات إلى قوائم الانتظار أو تحتاج إلى أن تتم إزالتها. هناك العديد من المجموعات الممكنة هنا ؛ مربع أبيض بعيدا. لا يزال يتطلب خيوط وهمية متعددة لإجراء مكالمات إلى الكائن قيد الاختبار.

جانبا:

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


يمكن أن تكون الفائدة أيضًا مفيدة لمساعدتك في كتابة اختبارات الوحدة الحتمية. انها تسمح لك الانتظار حتى يتم تحديث بعض دولة في مكان ما في النظام الخاص بك. فمثلا:

await().untilCall( to(myService).myMethod(), greaterThan(3) );

أو

await().atMost(5,SECONDS).until(fieldIn(myObject).ofType(int.class), equalTo(1));

كما أن لديها دعم سكالا و Groovy.

await until { something() > 4 } // Scala example

لدى Pete Goodliffe سلسلة حول اختبار وحدة الشفرة المترابطة .

من الصعب. أنا أسهل طريقة للخروج ومحاولة للحفاظ على رمز مؤشر الترابط المستخرج من الاختبار الفعلي. ذكر بيت أن الطريقة التي أقوم بها بها خاطئة ، لكني إما حصلت على الفصل الصحيح أو كنت محظوظة.





unit-testing