Sinon: فكرة لمراحل المستقبل (ق)

تم إنشاؤها على ١٣ سبتمبر ٢٠١٧  ·  31تعليقات  ·  مصدر: sinonjs/sinon

خلفية

أصبح sinon.stub / sandbox.stub حوض المطبخ لـ
قابل للتكوين مع المشكلات التي غالبًا ما يصعب العثور عليها وإصلاحها دون حدوث تراجع.

أعتقد أن السبب الجذري للصعوبات هو أنه يتحمل مسؤوليات كثيرة stub .

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

var myStub;

beforeEach(function(){
    myStub = sinon.stub().resolves('apple pie :)');
});

// several hundred lines of tests later
myStub = sinon.stub().rejects('no more pie :(');

// several hundred lines of tests later
// what behaviour does myStub currently have? Can you tell without 
// reading the entire file?
// can you safely change the behaviour without affecting tests further 
// down in the file?

ثم هناك السيناريوهات الأكثر إرباكًا

var myStub = sinon.stub()
                .withArgs(42)
                .onThirdCall()
                .resolves('apple pie')
                .rejects('no more pie')

ماذا يفعل ذلك حتى؟

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

أهم ما يمكنني التفكير فيه هو الوقوف الثابت لوظيفة ما.

يمكننا بعد ذلك معرفة ما سنفعله بشأن الممتلكات (كعضو منفصل وجديد ومسؤول واحد).

sinon.fake

A fake (القيمة المرجعة للاتصال بـ sinon.fake ) هي قيمة خالصة وثابتة Function . إنه يفعل شيئًا واحدًا وشيءًا واحدًا فقط. له نفس السلوك في كل مكالمة. بخلاف stub ، لا يمكن إعادة تعريف سلوكه. إذا كنت بحاجة إلى سلوك مختلف ، فاكسب fake جديدًا.

مسؤولية واحدة

يمكن أن يكون للمزيف إحدى هذه المسؤوليات

  • حل قيمة Promise إلى قيمة
  • رفض Promise إلى Error
  • إرجاع قيمة
  • رمي Error
  • ينتج عن قيمة (قيم) رد اتصال

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

sinon.replace(myObject, myMethod, sandbox.spy(function(args) {
    someFunctionWithSideEffects(args);
});

يلقي أخطاء بسخاء

سيكون كريمًا مع إلقاء الأخطاء عندما يحاول المستخدم إنشاءها / استخدامها بطرق غير مدعومة.

// will throw TypeError when `config` argument has more than one property
const fake = sinon.fake({
    resolves: true, 
    returns: true
});

يستخدم تجسس API

باستثناء .withArgs ، لأن ذلك ينتهك الثبات

أفكار الاستخدام

// will return a Promise that resolves to 'apple pie'
var fake = sinon.fake({resolves: 'apple pie'})

// will return a Promise that rejects with the provided Error, or 
// creates a generic Error using the input as message
var fake = sinon.fake({rejects: new TypeError('no more pie')});
var fake = sinon.fake({rejects: 'no more pie'});

// returns the value passed
var fake = sinon.fake({returns: 'apple pie'});

// throws the provided Error, or creates a generic Error using the 
// input as message
var fake = sinon.fake({throws: new RangeError('no more pie')});
var fake = sinon.fake({throws: 'no more pie'});

// replace a method with a fake
var fake = sinon.replace(myObject, 'methodName', sandbox.fake({
    returns: 'apple pie'
}))
// .. or use the helper method, which will use `sandbox.replace` and `
// sinon.fake`
var fake = sinon.setFake(global, 'methodName', {
    returns: 'apple pie'
});

// create an async fake
var asyncFake = sinon.asyncFake({
    returns: 'apple pie'
});

المرادفات

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

تغييرات API المقترحة

استخدم وضع الحماية الافتراضي

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

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

sandbox.replace

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

من المحتمل أن يكون لهذا بعض التحقق الجاد من صحة المدخلات ، لذلك لن يحل محل الوظائف إلا بالوظائف ، والموصلات مع الموصلات ، وما إلى ذلك.

Feature Request Improvement Needs investigation pinned

التعليق الأكثر فائدة

حسنًا ، أعتقد أن لدينا فهمًا مشابهًا 👍

فقط للتكرار ، في حالة فقدنا لشيء ما وحتى يكون لدى المساهمين الآخرين نفس الفهم.

TL ؛ DR

  • سيتم إجراء جميع عمليات الاستبدال بواسطة أداة مساعدة جديدة: sandbox.replace (هذا موجود حاليًا في stub )
  • سيكون لدى sinon وضع حماية افتراضي ، مما يسمح بـ sinon.reset و sinon.restore (هل يجب علينا دمج هذه؟)
  • sinon.fake - بديل قابل للبرمجة وغير قابل للتغيير للوظائف التي تسجل جميع المكالمات
  • sinon.spy
  • sinon.stub
// effectively a spy that has no target
const fake = sinon.fake()

// spy on a function
const fake = sinon.fake(console.log);
const fake = sinon.fake(function() { return 'apple pie'; });

// a shorthand construction of fake with behaviour
const fake = sinon.fake({
    returns: 'apple pie'
});

// replacing an existing function with a fake
var fakeLog = sinon.fake();
sandbox.replace(console, 'log', fakeLog);

في هذه الحالة ، يتعامل الاقتراح مع Function فقط. نحن بحاجة إلى التفكير فيما يجب فعله بشأن الخصائص غير الوظيفية والموصِّلات. على الأقل ، يجب أن نرى ما إذا كان بإمكاننا تقييد sandbox.replace للسماح فقط بالبدائل المعقولة.

ال 31 كومينتر

بينغ @ sinonjs / الأساسية

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

سينون

أوافق على أن الثبات هو المفتاح هنا. يمكن أن نسمح ببعض حالات الاستخدام "العاقلة" التي تكون ممكنة حاليًا مع بذرة.

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

sinon.fake({
  yields: [null, 42],
  returns: true
})

يمكننا التحقق مما هو منطقي وما هو غير منطقي.

أيضًا ، إذا كنا ندعم callsThrough: true كتكوين (وهو غير صالح مع أي من خصائص السلوكيات) ، فيمكن أيضًا استخدام المزيف الجديد بدلاً من واجهة برمجة التطبيقات "spy". سيكون هذا شرحًا ذاتيًا أكثر من تعلم معنى "جاسوس" و "كعب" في Sinon-talk 😄

استخدم وضع الحماية الافتراضي

بينما تعجبني هذه الفكرة ، فهذا يعني أن استدعاء sinon.restore() بعد الاختبار يمكن أن يعيد بعض الفائض من الاختبارات الأخرى ويؤدي إلى نتائج مفاجئة - أو فشل الاختبارات التي حدثت من قبل. الشيء الرائع الذي سيمكنه هذا هو إعادة تعيين وضع الحماية العالمي في beforeEach لتحسين عزل الاختبار. 👍

وضع الحماية

أنا أحب هذا كثيرا. أفهم هذا على أنه أداة مساعدة "دعني ألصق هذا الشيء هناك" ، أليس كذلك؟

أيضًا ، إذا كنا ندعم callThrough: true كإعداد (وهو غير صالح مع أي من خصائص السلوكيات) ، فيمكن أيضًا استخدام المزيفات الجديدة بدلاً من واجهة برمجة التطبيقات "spy". سيكون هذا شرحًا ذاتيًا أكثر من تعلم معنى "جاسوس" و "كعب" في Sinon-talk 😄

هل يعني ذلك أننا لن نحتاج إلى spy أو stub على الإطلاق؟

وضع الحماية

أنا أحب هذا كثيرا. أفهم هذا على أنه أداة مساعدة "دعني ألصق هذا الشيء هناك" ، أليس كذلك؟

نعم ، كانت هذه هي الفكرة. بدلاً من التحميل الزائد على نفس الطريقة ( sinon.stub ) للقيام بأشياء كثيرة ، يكون لديك طرق صريحة تقوم بشيء واحد فقط

كما أوضحت ، فإن واجهة برمجة تطبيقات fake ربما لن تدعم كل ما هو ممكن حاليًا مع الجواسيس والأعراف. لكن نعم ، أعتقد أن واجهة برمجة التطبيقات fake هي فرصة لتوحيد وظائف stub و spy .

بينما تعجبني هذه الفكرة ، فهذا يعني أن استدعاء sinon.restore () بعد الاختبار يمكن أن يعيد بعض الفائض من الاختبارات الأخرى ويؤدي إلى نتائج مفاجئة - أو فشل الاختبارات التي حدثت من قبل. الشيء الرائع الذي سيمكنه هذا هو إعادة تعيين وضع الحماية العالمي قبل كل شيء لتحسين عزل الاختبار. 👍

إنه بالتأكيد تغيير جذري ، ولا ينبغي تقديمه باستخفاف.

عند إنشاء fake ، إذا لم تمرره لتكوين سلوك ، فسيكون مكافئًا لـ spy .

// ~spy, records all calls, has no behaviour
const fake = sinon.fake();

// ~stub, records all calls, returns 'apple pie'
const fake = sinon.fake({
    returns: 'apple pie'
});

كيف يمكنك إنشاء كعب لا يفعل شيئًا بعد ذلك؟

كيف يمكنك إنشاء كعب لا يفعل شيئًا بعد ذلك؟

لست متأكدًا من أنني أفهم سؤالك تمامًا ... ولكن هنا

// a fake that has no behaviour
const fake = sinon.fake();

// put it in place of an existing method
sandbox.replace(myObject, 'someMethod', fake);

آه ، أعتقد أنني فهمت ما تعنيه الآن: A fake دائمًا stub . عندما قلت ~spy, records all calls فهمت "المكالمات من خلال الوظيفة الأصلية". ومع ذلك ، فإن fake ليس لديه معرفة بالوظيفة التي يستبدلها - وهذا ما يفعله sandbox.replace .

مع أخذ ذلك في الاعتبار ، إليك اقتراح آخر حول كيفية طي الوظيفة الحالية spy (كما هو الحال في الاتصال من خلال) في المنتجات المقلدة الجديدة:

const fake = sinon.fake(function () {
   // Any custom function
});

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

يمكن أن يصبح تنفيذ sandbox.spy(object, method) كما يلي:

const original = object[method];
const fake = sinon.fake(original);
sandbox.replace(object, method, fake);

في الأساس خط واحد 🤓

نعم. بمجرد تبسيط الأشياء ، يمكنك البدء في إعادة الخلط من أجل المتعة 🎉 والربح 💰

ومع ذلك ، إذا أردنا الانجذاب نحو استخدام fake ولم نعد نستخدم spy و stub ، فربما يجب علينا ترك هذين الاثنين بمفردهما.

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

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

لست متأكدا من أنني أتابع. هل يمكن أن تتطور؟

بالتأكيد. كما أفهم اقتراحك ، فأنت تريد بديلاً لواجهة برمجة التطبيقات stub شديدة التعقيد. الطريقة التي يتم بها تنفيذ الروتين حاليًا هي إنشاء spy مع وظيفة تقوم بتنفيذ السلوك. ما أقترحه هو القيام بنفس الشيء باستخدام واجهة برمجة التطبيقات fake وإنشاء spy داخليًا ، لكننا لن نعيد سلوكًا بعد الآن ، لأننا نريد التخلص من التسلسل . نحن فقط نعيد الجاسوس. هذا يجعل تطبيق fake بديلاً عن stub مع كون الوظيفة المرتجعة متوافقة مع جميع واجهات برمجة تطبيقات Sinon الحالية. هل هذا منطقي أم أنني أفتقد شيئًا؟

حسنًا ، أعتقد أن لدينا فهمًا مشابهًا 👍

فقط للتكرار ، في حالة فقدنا لشيء ما وحتى يكون لدى المساهمين الآخرين نفس الفهم.

TL ؛ DR

  • سيتم إجراء جميع عمليات الاستبدال بواسطة أداة مساعدة جديدة: sandbox.replace (هذا موجود حاليًا في stub )
  • سيكون لدى sinon وضع حماية افتراضي ، مما يسمح بـ sinon.reset و sinon.restore (هل يجب علينا دمج هذه؟)
  • sinon.fake - بديل قابل للبرمجة وغير قابل للتغيير للوظائف التي تسجل جميع المكالمات
  • sinon.spy
  • sinon.stub
// effectively a spy that has no target
const fake = sinon.fake()

// spy on a function
const fake = sinon.fake(console.log);
const fake = sinon.fake(function() { return 'apple pie'; });

// a shorthand construction of fake with behaviour
const fake = sinon.fake({
    returns: 'apple pie'
});

// replacing an existing function with a fake
var fakeLog = sinon.fake();
sandbox.replace(console, 'log', fakeLog);

في هذه الحالة ، يتعامل الاقتراح مع Function فقط. نحن بحاجة إلى التفكير فيما يجب فعله بشأن الخصائص غير الوظيفية والموصِّلات. على الأقل ، يجب أن نرى ما إذا كان بإمكاننا تقييد sandbox.replace للسماح فقط بالبدائل المعقولة.

هل هذا يعني أن sinon.stub() و sinon.spy() كلاهما سيتم إهماله في المستقبل لصالح sinon.fake() ، أو إعادة بنائه داخليًا فقط؟ إذا كان الأمر كذلك ، فإننا نتحرك أساسًا نحو تفكير TestDouble . ليس بالضرورة شيئًا سيئًا ، IMHO ، ولكن قد يكون من المفيد التفكير في أنه إذا وجد الكثير من الأشخاص أنهم سيحتاجون إلى استبدال جميع مكالمات Sinon api الخاصة بهم على أي حال مقابل sinon.fake() ، فقد يستخدمون مكتبة أخرى (على الرغم من هذا يعني أنهم سيفقدون كل معرفتهم الحالية بواجهة برمجة تطبيقات Sinon).

هل هذا يعني أن sinon.stub () و sinon.spy () سيتم إهماله في المستقبل لصالح sinon.fake () أو إعادة بنائه داخليًا؟ إذا كان الأمر كذلك ، فإننا نتحرك أساسًا نحو تفكير TestDouble.

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

ليس بالضرورة شيئًا سيئًا ، IMHO ، ولكن قد يكون من المفيد التفكير في أنه إذا وجد الكثير من الأشخاص أنهم سيحتاجون إلى استبدال جميع مكالمات Sinon api الخاصة بهم على أي حال لـ sinon.fake () ، فيمكنهم أيضًا استخدام مكتبة أخرى (على الرغم من ذلك سيعني أنهم سيفقدون كل معرفتهم الحالية بواجهة برمجة تطبيقات Sinon).

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

ولكن هل سنحتفظ بأساليب التجسس والخطأ أو نتخلى عنها ، مع بعض التخفيضات المحتملة في الوظائف؟ لم يكن ذلك واضحا بالنسبة لي.

ولكن هل سنحتفظ بأساليب التجسس والخطأ أو نتخلى عنها ، مع بعض التخفيضات المحتملة في الوظائف؟

بمجرد أن يبدو fake مستقرًا ، سأقوم بإهمال spy و stub ، ومن ثم أمنحه عامًا لإتاحة الوقت للناس للترقية.

أعتقد أننا يجب أن نبذل قصارى جهدنا لتوفير أنظمة تشفير ووثائق رائعة لمساعدة الأشخاص على نقل الكود الخاص بهم

أنا أعمل على فرع للأجزاء الأولى من هذا (وضع الحماية الافتراضي). لقد أعدت بناء الكود بحيث أصبح الآن sandbox و collection واحدًا. لقد حصلت على وضع الحماية الافتراضي يعمل.

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

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

أود أيضًا إضافة إخطارات الإهمال إلى الجواسيس والجواسيس.

كنت أفكر أيضًا في تغيير تمرير كائن بمفاتيح عن طريق تمرير الوظائف.

سيضيف هذا الفوائد التالية:

  • سيسمح لنا ذلك بإضافة type إلى تلك الوظائف للمستخدم الذي يريد استخدام typescript أو أي نوع آخر من أدوات الداما الثابتة
  • قد يحصل المستخدمون على أخطاء عند محاولة استدعاء وظائف لسلوكيات غير موجودة
  • يمكننا توثيق هذه الوظائف بشكل منفصل وتحسين المستندات
  • يمكننا تقديم أخطاء مفيدة عند تمرير الحجج التي لا معنى لها لتلك السلوكيات والسماح لها بالحصول على أكثر من وسيطة اختيارية
  • سيجعل الأشياء أكثر قابلية للتكوين أيضًا (على الرغم من أنني لا أرى العديد من الحالات لهذا في هذه الحالة) ويسمح للأشخاص بإعادة استخدام السلوكيات التي تم إنشاؤها
  • IMO سيكون هذا أيضًا أبسط من وجود كائن به سلوك

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

// It would be cool to allow users to import these using destructuring to make code more concise
import { resolves, rejects, returns } from 'sinon/behaviors'; 

var fake = sinon.fake(resolves('apple pie'))

var fake = sinon.fake(rejects(new TypeError('no more pie')));
var fake = sinon.fake(rejects('no more pie'));

var fake = sinon.fake(returns('apple pie'));

var fake = sinon.fake(throws(new RangeError('no more pie'));
var fake = sinon.fake(throws('no more pie'));

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

أيضًا ، عندما يتعلق الأمر بخلط أشياء مثل onThirdCall و withArgs أعتقد أنه يجب توثيق ما يحدث في هذه الحالات.

آسف لمراجعة هذا في وقت متأخر جدا. كانت الأشهر القليلة الماضية مشغولة للغاية.

lucasfcosta تحقق من PR # 1586

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

الإصدار 5.0.0 السابق يسبب مشاكل مع الإصدارات الأحدث 5.0.0-next. * prerelease في package.json لأن 5.0.0 أكبر من أي إصدار تجريبي.

نظرًا لأن الإصدار 5.0.0 متوفر ، أعتقد أن الأرقام التجريبية للإصدار التجريبي من next تحتاج إلى الارتطام ربما إلى 5.0.1-next.1 ؟

لقد لاحظت هذا لأن حزمة أخرى كنت أستخدمها كانت تتلقى رسالة مهملة وتعتمد الحزمة json على "sinon": "^5.0.0-next.4"

npm WARN deprecated [email protected]: this version has been deprecated

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

الحل الآخر هو إطلاق الإصدار الرئيسي التالي. ما رأيك @ sinonjs / الأساسية؟

mroderick لا أستطيع أن أقول بعد الآن ما هي جميع التغييرات لـ v5. من اختباراتي الأخيرة ، عملت بشكل جيد وأنا أتطلع إلى استخدام المنتجات المقلدة الجديدة. إنه تخصص جديد ، لذا ، اشحنه 😄

هناك واحد فقط PR # 1764 أود دمجها ، قبل أن نطلق الإصدار الرئيسي التالي.

لقد قمت بنشر [email protected] ، آمل أن يجعل ذلك الحياة أسهل للناس في هذه الأثناء.

شكرًا ، لقد اختبرت (من الجيد دائمًا التحقق مرة أخرى) التبعيات في package.json و "sinon": "^5.0.1" يعطي خطأ كما ينبغي لأنه لم يتم العثور على تطابق (لم يتم العثور على إصدار حتى الآن) ، و "sinon": "^5.0.1-next.1" يعمل بشكل صحيح في الحصول على هذا الإصدار.

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

تم تقديم fake مع # 1768 ، والذي أصبح [email protected]

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات