كل ما تحتاجه للبدء هو مجرد إضافة تبعية إلى مكتبة MockK .
| يقترب | تعليمات |
|---|---|
![]() | شهادة "io.mockk: mockk: $ {mockkversion}"
|
(Kotlin DSL) | شهادة ("io.mockk: mockk: $ {mockkversion}") |
![]() | <premency>
<roupl> io.mockk </rougiD>
<StifactId> mockk-jvm </stifactid>
<splect> $ {mockkversion} </version>
<scope> اختبار </scope>
</التبعية>
|
شهادة "io.mockk: Mockk-Android: $ {mockkversion}"
شهادة "io.mockk: Mockk-Agent: $ {mockkversion}"
| |
AndroidTestImplementation "io.mockk: Mockk-Android: $ {mockkversion}"
AndroidTestimplementation "io.mockk: Mockk-Agent: $ {Mockkversion}"
|
أبسط مثال. بشكل افتراضي ، تعتبر السخرية صارمة ، لذلك تحتاج إلى توفير بعض السلوك.
val car = mockk< Car >()
every { car.drive( Direction . NORTH ) } returns Outcome . OK
car.drive( Direction . NORTH ) // returns OK
verify { car.drive( Direction . NORTH ) }
confirmVerified(car)راجع قسم "الميزات" أدناه للحصول على أمثلة أكثر تفصيلاً.
من الإصدار 1.13.0 يدعم Mockk Kotlin 1.4 وأعلى
mockkStatic على JDK 16+ ؛ InaccessibleObjectException IllegalAccessExceptionجدول المحتويات:
تحقق من سلسلة المقالات "السخرية ليست علوم الصواريخ" في KT. أكاديمية تصف موكك من أساسيات السخرية حتى وصف جميع الميزات المتقدمة.
الأساسيات
السلوك المتوقع والتحقق من السلوك
ميزات موكك
ميزات Mockk المتقدمة
اختبار Quarkus مع Kotlin و Junit و Mockk
كشف السحر الأسود Mockk (EN ، الترجمة)
دليل Mockk
"اختبار وحدة Kotlin مع Mockk" بقلم Marco Cattaneo
(فيديو) استخدم التحقق في Mockk للتحقق من مكالمات الوظائف على كائن مسخر
اختبار مع Mockk Pay
TDD لـ Android Video Tutorial Part 1 ، الجزء 2 من تأليف Ryan Kay
(فيديو) Android Developer Live Coding #13: اختبار الوحدة مع Mockk ، Coroutines ، التطوير الذي يحركه الاختبار
Kotlinconf 2018 - أفضل الممارسات لاختبار الوحدة في Kotlin بواسطة Philipp Hauer
يستخدم Kotlin-Fullstack-Sample مشروع Mockk المغطى باختبارات
مقال dzone
مقالة هابراهااب (رو)
يسخر في Kotlin مع Mockk - Yannick de Turck
يمكنك استخدام التعليقات التوضيحية لتبسيط إنشاء كائنات وهمية:
class TrafficSystem {
lateinit var car1 : Car
lateinit var car2 : Car
lateinit var car3 : Car
}
class CarTest {
@MockK
lateinit var car1 : Car
@RelaxedMockK
lateinit var car2 : Car
@MockK(relaxUnitFun = true )
lateinit var car3 : Car
@SpyK
var car4 = Car ()
@InjectMockKs
var trafficSystem = TrafficSystem ()
@Before
fun setUp () = MockKAnnotations . init ( this , relaxUnitFun = true ) // turn relaxUnitFun on for all mocks
@Test
fun calculateAddsValues1 () {
// ... use car1, car2, car3 and car4
}
} يحاول الحقن أولاً مطابقة الخصائص بالاسم ، ثم حسب الفصل أو الفئة الفائقة. تحقق من معلمة lookupType للتخصيص.
يتم حقن الخصائص حتى لو تم تطبيق private . يتم اختيار مُنشئو الحقن من أكبر عدد من الحجج إلى أدنى مستوى.
@InjectMockKs بشكل افتراضي الحقن فقط lateinit var s أو var s لم يتم تعيينه. لتغيير هذا ، استخدم overrideValues = true . هذا من شأنه أن يعين القيمة حتى لو تم تهيئته بالفعل بطريقة أو بأخرى. لحقن val s ، استخدم injectImmutable = true . للحصول على تدوين أقصر ، استخدم @OverrideMockKs الذي يفعل نفس الشيء مثل @InjectMockKs بشكل افتراضي ، ولكنه يعمل على تشغيل هاتين العلامات.
يعرض Junit 4 واجهة برمجة تطبيقات قائمة على القواعد للسماح ببعض الأتمتة بعد دورة حياة الاختبار. يتضمن Mockk قاعدة تستخدم هذا لإعداد وهدم السخرية الخاصة بك دون الحاجة إلى الاتصال يدويًا MockKAnnotations.init(this) . مثال:
class CarTest {
@get:Rule
val mockkRule = MockKRule ( this )
@MockK
lateinit var car1 : Car
@RelaxedMockK
lateinit var car2 : Car
@Test
fun something () {
every { car1.drive() } just runs
every { car2.changeGear(any()) } returns true
// etc
}
} في JUNIT5 ، يمكنك استخدام MockKExtension لتهيئة السخرية.
@ExtendWith( MockKExtension :: class )
class CarTest {
@MockK
lateinit var car1 : Car
@RelaxedMockK
lateinit var car2 : Car
@MockK(relaxUnitFun = true )
lateinit var car3 : Car
@SpyK
var car4 = Car ()
@Test
fun calculateAddsValues1 () {
// ... use car1, car2, car3 and car4
}
} بالإضافة إلى ذلك ، فإنه يضيف إمكانية استخدام @MockK و @RelaxedMockK على معلمات دالة الاختبار:
@Test
fun calculateAddsValues1 (@MockK car1 : Car , @RelaxedMockK car2 : Car ) {
// ... use car1 and car2
} أخيرًا ، سيتصل هذا الملحق بـ unmockkAll و clearAllMocks في رد اتصال @AfterAll ، مما يضمن أن بيئة الاختبار نظيفة بعد كل تنفيذ من فئة الاختبار. يمكنك تعطيل هذا السلوك عن طريق إضافة @MockKExtension.KeepMocks التعليقات التوضيحية إلى صفك أو على مستوى العالم من خلال تعيين mockk.junit.extension.keepmocks=true . (منذ V1.13.11) بدلاً من ذلك ، نظرًا لأن clearAllMocks افتراضيًا ( currentThreadOnly=false ) ليس آمنًا للموضوع ، إذا كنت بحاجة إلى إجراء اختبار بالتوازي ، فيمكنك إضافة MockKExtension.RequireParallelTesting التعليقات mockk.junit.extension.requireParallelTesting=true إلى callbring @AfterAll إذا تم استدعاء clearAllMocks بشكل صريح ، فيمكنك توفير clearAllMocks(currentThreadOnly = true) بحيث يقوم فقط بمسح النسيج التي تم إنشاؤها داخل نفس الخيط (منذ V1.13.12).
يمكنك التأكد من أن جميع الأساليب التي يتم التعبير عنها يتم التحقق منها بالفعل عن طريق التعليق أيضًا على فئة الاختبار الخاصة بك باستخدام @MockKExtension.ConfirmVerification .
سيؤدي ذلك إلى confirmVerified داخليًا على جميع النماذج بعد كل اختبار ، للتأكد من عدم وجود أصغام غير ضرورية.
يرجى ملاحظة أن هذا السلوك قد لا يعمل كما هو متوقع عند إجراء الاختبارات في IDE ، حيث إن Gradle هو الذي يهتم بالتعامل مع الاستثناء الذي يتم طرحه عند فشل هذه المكالمات confirmVerified .
يمكنك التأكد من أن جميع الأساليب البطيئة مفيدة - مرة واحدة على الأقل - عن طريق التعليق أيضًا على فئة الاختبار الخاصة بك مع @MockKExtension.CheckUnnecessaryStub .
سيؤدي ذلك إلى الاتصال داخليًا checkUnnecessaryStub على جميع النماذج بعد كل اختبار ، للتأكد من عدم وجود أصحاب غير ضروريين.
جواسيس يسمح لك بخلط السخرية والأشياء الحقيقية.
val car = spyk( Car ()) // or spyk<Car>() to call the default constructor
car.drive( Direction . NORTH ) // returns whatever the real function of Car returns
verify { car.drive( Direction . NORTH ) }
confirmVerified(car)ملاحظة 1: كائن التجسس هو نسخة من الكائن الذي تم تمريره. الملاحظة 2: هناك مشكلة معروفة في حالة استخدام جاسوس مع وظيفة تعليق: #554
relaxed mock هي الوهمية التي تُرجع بعض القيمة البسيطة لجميع الوظائف. يتيح لك ذلك تخطي تحديد السلوك لكل حالة ، بينما لا تزال تزعج الأشياء التي تحتاجها. للحصول على أنواع مرجعية ، يتم إرجاع السخرية بالسلاسل.
val car = mockk< Car >(relaxed = true )
car.drive( Direction . NORTH ) // returns null
verify { car.drive( Direction . NORTH ) }
confirmVerified(car)ملاحظة: السخرية المريحة تعمل بشكل سيء مع أنواع الإرجاع العامة. عادة ما يتم طرح استثناء من الفصل في هذه الحالة. اختر الإقصاء يدويًا في حالة نوع الإرجاع العام.
الحل البديل:
val func = mockk < () -> Car > (relaxed = true ) // in this case invoke function has generic return type
// this line is workaround, without it the relaxed mock would throw a class cast exception on the next line
every { func() } returns Car () // or you can return mockk() for example
func() في بعض الأحيان ، تحتاج إلى تعبئة بعض الوظائف ، ولكن لا تزال تتصل بالطريقة الحقيقية للآخرين ، أو على وسيط محددة. هذا ممكن عن طريق تمرير callOriginal() إلى answers ، والتي تعمل مع كل من السخرية المريحة وغير المريحة.
class Adder {
fun addOne ( num : Int ) = num + 1
}
val adder = mockk< Adder >()
every { adder.addOne(any()) } returns - 1
every { adder.addOne( 3 ) } answers { callOriginal() }
assertEquals( - 1 , adder.addOne( 2 ))
assertEquals( 4 , adder.addOne( 3 )) // original function is called إذا كنت تريد أن يتم استرخاء وظائف Unit ، فيمكنك استخدام relaxUnitFun = true كوسيطة لوظيفة mockk أو التعليق التوضيحي @MockK أو MockKAnnotations.init .
وظيفة:
mockk< ClassBeingMocked >(relaxUnitFun = true )التعليق التوضيحي:
@MockK(relaxUnitFun = true )
lateinit var mock1 : ClassBeingMocked
init {
MockKAnnotations . init ( this )
}mockkannotations.init:
@MockK
lateinit var mock2 : ClassBeingMocked
init {
MockKAnnotations . init ( this , relaxUnitFun = true )
}يمكن أن تتحول الكائنات إلى وهمية بالطريقة التالية:
object ObjBeingMocked {
fun add ( a : Int , b : Int ) = a + b
}
mockkObject( ObjBeingMocked ) // applies mocking to an Object
assertEquals( 3 , ObjBeingMocked .add( 1 , 2 ))
every { ObjBeingMocked .add( 1 , 2 ) } returns 55
assertEquals( 55 , ObjBeingMocked .add( 1 , 2 )) للعودة إلى الوراء ، استخدم unmockkObject أو unmockkAll (أكثر تدميراً: إلغاء الكائنات ، السخرية الثابتة والمركزية)
@Before
fun beforeTests () {
mockkObject( ObjBeingMocked )
every { ObjBeingMocked .add( 1 , 2 ) } returns 55
}
@Test
fun willUseMockBehaviour () {
assertEquals( 55 , ObjBeingMocked .add( 1 , 2 ))
}
@After
fun afterTests () {
unmockkObject( ObjBeingMocked )
// or unmockkAll()
}على الرغم من قيود لغة Kotlin ، يمكنك إنشاء مثيلات جديدة من الكائنات إذا لزم الأمر عن طريق اختبار المنطق:
val newObjectMock = mockk< ObjBeingMocked >() في بعض الأحيان تحتاج إلى وهمية من فئة تعسفية. استخدام mockkClass في تلك الحالات.
val car = mockkClass( Car :: class )
every { car.drive( Direction . NORTH ) } returns Outcome . OK
car.drive( Direction . NORTH ) // returns OK
verify { car.drive( Direction . NORTH ) } يمكن أن تسخر من التعدادات باستخدام mockkObject :
enum class Enumeration ( val goodInt : Int ) {
CONSTANT ( 35 ),
OTHER_CONSTANT ( 45 );
}
mockkObject( Enumeration . CONSTANT )
every { Enumeration . CONSTANT .goodInt } returns 42
assertEquals( 42 , Enumeration . CONSTANT .goodInt)في بعض الأحيان ، خاصة في التعليمات البرمجية التي لا تملكها ، تحتاج إلى السخرية من الكائنات التي تم إنشاؤها حديثًا. لهذا الغرض ، يتم توفير التركيبات التالية:
class MockCls {
fun add ( a : Int , b : Int ) = a + b
}
mockkConstructor( MockCls :: class )
every { anyConstructed< MockCls >().add( 1 , 2 ) } returns 4
assertEquals( 4 , MockCls ().add( 1 , 2 )) // note new object is created
verify { anyConstructed< MockCls >().add( 1 , 2 ) } الفكرة الأساسية هي أنه بعد تنفيذ مُنشئ الفئة السهلة (أي منها) ، تصبح الأشياء constructed mock .
يرتبط السلوك السخري لمثل هذا الوهمية prototype mock الخاصة التي يدل عليها anyConstructed<MockCls>() .
هناك مثيل واحد لكل فئة من هذا prototype mock . تسجيل المكالمات يحدث أيضا لنموذج prototype mock .
إذا لم يتم تحديد أي سلوك للوظيفة ، فسيتم تنفيذ الوظيفة الأصلية.
في حالة وجود فئة أكثر من مُنشئ ، يمكن استيعاب كل منها بشكل منفصل:
class MockCls ( private val a : Int = 0 ) {
constructor (x : String ) : this (x.toInt())
fun add ( b : Int ) = a + b
}
mockkConstructor( MockCls :: class )
every { constructedWith< MockCls >().add( 1 ) } returns 2
every {
constructedWith< MockCls >( OfTypeMatcher < String >( String :: class )).add( 2 ) // Mocks the constructor which takes a String
} returns 3
every {
constructedWith< MockCls >( EqMatcher ( 4 )).add(any()) // Mocks the constructor which takes an Int
} returns 4
assertEquals( 2 , MockCls ().add( 1 ))
assertEquals( 3 , MockCls ( " 2 " ).add( 2 ))
assertEquals( 4 , MockCls ( 4 ).add( 7 ))
verify {
constructedWith< MockCls >().add( 1 )
constructedWith< MockCls >( " 2 " ).add( 2 )
constructedWith< MockCls >( EqMatcher ( 4 )).add( 7 )
} لاحظ أنه في هذه الحالة ، يتم إنشاء prototype mock لكل مجموعة من مطاعم الوسيطة التي تم تمريرها إلى constructedWith .
يمكنك مزج كل من الحجج والمطابقات العادية:
val car = mockk< Car >()
every {
car.recordTelemetry(
speed = more( 50 ),
direction = Direction . NORTH , // here eq() is used
lat = any(),
long = any()
)
} returns Outcome . RECORDED
car.recordTelemetry( 60 , Direction . NORTH , 51.1377382 , 17.0257142 )
verify { car.recordTelemetry( 60 , Direction . NORTH , 51.1377382 , 17.0257142 ) }
confirmVerified(car)يمكنك تعبئة سلاسل المكالمات:
val car = mockk< Car >()
every { car.door( DoorType . FRONT_LEFT ).windowState() } returns WindowState . UP
car.door( DoorType . FRONT_LEFT ) // returns chained mock for Door
car.door( DoorType . FRONT_LEFT ).windowState() // returns WindowState.UP
verify { car.door( DoorType . FRONT_LEFT ).windowState() }
confirmVerified(car) ملاحظة: إذا كان نوع إرجاع الوظيفة عامًا عامًا ، فستختفي المعلومات حول النوع الفعلي.
لإجراء مكالمات بالسلاسل ، مطلوب معلومات إضافية.
في معظم الأوقات ، سيقوم الإطار بالقبض على استثناء المصبوب ويقوم autohinting .
في حالة من المطلوب صراحة ، استخدم hint قبل إجراء المكالمة التالية.
every { obj.op2( 1 , 2 ).hint( Int :: class ).op1( 3 , 4 ) } returns 5من الإصدار 1.9.1 قد يتم التسلسل في التسلسل الهرمي:
interface AddressBook {
val contacts : List < Contact >
}
interface Contact {
val name : String
val telephone : String
val address : Address
}
interface Address {
val city : String
val zip : String
}
val addressBook = mockk< AddressBook > {
every { contacts } returns listOf (
mockk {
every { name } returns " John "
every { telephone } returns " 123-456-789 "
every { address.city } returns " New-York "
every { address.zip } returns " 123-45 "
},
mockk {
every { name } returns " Alex "
every { telephone } returns " 789-456-123 "
every { address } returns mockk {
every { city } returns " Wroclaw "
every { zip } returns " 543-21 "
}
}
)
} يمكنك التقاط حجة إلى CapturingSlot أو MutableList .
عادةً ما يتم إنشاء CapturingSlot عبر slot<T : Any?>() يهدف MutableList لالتقاط قيم متعددة أثناء الاختبار.
enum class Direction { NORTH , SOUTH }
enum class RecordingOutcome { RECORDED }
enum class RoadType { HIGHWAY }
class Car {
fun recordTelemetry ( speed : Double , direction : Direction , roadType : RoadType ? ): RecordingOutcome {
TODO ( " not implement for showcase " )
}
}
val car = mockk< Car >()
// allow to capture parameter with non nullable type `Double`
val speedSlot = slot< Double >()
// allow to capture parameter with nullable type `RoadType`
val roadTypeSlot = slot< RoadType ?>()
val list = mutableListOf< Double >()
every {
car.recordTelemetry(
speed = capture(speedSlot), // makes mock match calls with any value for `speed` and record it in a slot
direction = Direction . NORTH , // makes mock and capturing only match calls with specific `direction`. Use `any()` to match calls with any `direction`
roadType = captureNullable(roadTypeSlot), // makes mock match calls with any value for `roadType` and record it in a slot
)
} answers {
println ( " Speed: ${speedSlot.captured} , roadType: ${roadTypeSlot.captured} " )
RecordingOutcome . RECORDED
}
every {
car.recordTelemetry(
speed = capture(list),
direction = Direction . SOUTH ,
roadType = captureNullable(roadTypeSlot),
)
} answers {
println ( " Speed: ${list} , roadType: ${roadTypeSlot.captured} " )
RecordingOutcome . RECORDED
}
car.recordTelemetry(speed = 15.0 , direction = Direction . NORTH , null ) // prints Speed: 15.0, roadType: null
car.recordTelemetry(speed = 16.0 , direction = Direction . SOUTH , RoadType . HIGHWAY ) // prints Speed: [16.0], roadType: HIGHWAY
verifyOrder {
car.recordTelemetry(speed = or ( 15.0 , 16.0 ), direction = any(), roadType = null )
car.recordTelemetry(speed = 16.0 , direction = any(), roadType = RoadType . HIGHWAY )
}
confirmVerified(car) يمكنك التحقق من عدد المكالمات مع المعلمات على atLeast أو atMost أو exactly :
val car = mockk< Car >(relaxed = true )
car.accelerate(fromSpeed = 10 , toSpeed = 20 )
car.accelerate(fromSpeed = 10 , toSpeed = 30 )
car.accelerate(fromSpeed = 20 , toSpeed = 30 )
// all pass
verify(atLeast = 3 ) { car.accelerate(allAny()) }
verify(atMost = 2 ) { car.accelerate(fromSpeed = 10 , toSpeed = or ( 20 , 30 )) }
verify(exactly = 1 ) { car.accelerate(fromSpeed = 10 , toSpeed = 20 ) }
verify(exactly = 0 ) { car.accelerate(fromSpeed = 30 , toSpeed = 10 ) } // means no calls were performed
confirmVerified(car) أو يمكنك استخدام verifyCount :
val car = mockk< Car >(relaxed = true )
car.accelerate(fromSpeed = 10 , toSpeed = 20 )
car.accelerate(fromSpeed = 10 , toSpeed = 30 )
car.accelerate(fromSpeed = 20 , toSpeed = 30 )
// all pass
verifyCount {
( 3 .. 5 ) * { car.accelerate(allAny(), allAny()) } // same as verify(atLeast = 3, atMost = 5) { car.accelerate(allAny(), allAny()) }
1 * { car.accelerate(fromSpeed = 10 , toSpeed = 20 ) } // same as verify(exactly = 1) { car.accelerate(fromSpeed = 10, toSpeed = 20) }
0 * { car.accelerate(fromSpeed = 30 , toSpeed = 10 ) } // same as verify(exactly = 0) { car.accelerate(fromSpeed = 30, toSpeed = 10) }
}
confirmVerified(car)verifyAll من التحقق من حدوث جميع المكالمات دون التحقق من طلبها.verifySequence يتحقق من أن المكالمات حدثت في تسلسل محدد.verifyOrder التحقق من أن المكالمات حدثت بترتيب محدد.wasNot Called يتحقق من أن وهمية (أو قائمة السخرية) لم يتم استدعاؤها على الإطلاق. class MockedClass {
fun sum ( a : Int , b : Int ) = a + b
}
val obj = mockk< MockedClass >()
val slot = slot< Int >()
every {
obj.sum(any(), capture(slot))
} answers {
1 + firstArg< Int >() + slot.captured
}
obj.sum( 1 , 2 ) // returns 4
obj.sum( 1 , 3 ) // returns 5
obj.sum( 2 , 2 ) // returns 5
verifyAll {
obj.sum( 1 , 3 )
obj.sum( 1 , 2 )
obj.sum( 2 , 2 )
}
verifySequence {
obj.sum( 1 , 2 )
obj.sum( 1 , 3 )
obj.sum( 2 , 2 )
}
verifyOrder {
obj.sum( 1 , 2 )
obj.sum( 2 , 2 )
}
val obj2 = mockk< MockedClass >()
val obj3 = mockk< MockedClass >()
verify {
listOf (obj2, obj3) wasNot Called
}
confirmVerified(obj) للتحقق من أنه تم التحقق من جميع المكالمات عن طريق verify... بنيات ، يمكنك استخدام confirmVerified :
confirmVerified(mock1, mock2) ليس من المنطقي استخدامه للتحقق verifySequence verifyAll ، لأن طرق التحقق هذه تغطي بالفعل جميع المكالمات بشكل شامل مع التحقق.
سوف يلقي استثناء إذا كان هناك بعض المكالمات المتبقية دون التحقق.
يمكن استبعاد بعض المكالمات من هذا التأكيد ، تحقق من القسم التالي لمزيد من التفاصيل.
val car = mockk< Car >()
every { car.drive( Direction . NORTH ) } returns Outcome . OK
every { car.drive( Direction . SOUTH ) } returns Outcome . OK
car.drive( Direction . NORTH ) // returns OK
car.drive( Direction . SOUTH ) // returns OK
verify {
car.drive( Direction . SOUTH )
car.drive( Direction . NORTH )
}
confirmVerified(car) // makes sure all calls were covered with verificationنظرًا لأن رمز الاختبار النظيف والقابل للصيانة يتطلب رمزًا غير ضروري صفريًا ، يمكنك التأكد من عدم وجود كعب غير ضروري.
checkUnnecessaryStub(mock1, mock2)سوف يلقي استثناء إذا كانت هناك بعض المكالمات المعلنة على السخرية التي لا تستخدمها الرمز الذي تم اختباره. يمكن أن يحدث هذا إذا كنت قد أعلنت عن بعض كعبان غير ضرورية أو إذا لم يتصل الرمز الذي تم اختباره برمز متوقع.
لاستبعاد المكالمات غير المهمة من التسجيل ، يمكنك استخدام excludeRecords :
excludeRecords { mock.operation(any(), 5 ) } سيتم استبعاد جميع المكالمات المطابقة من التسجيل. قد يكون هذا مفيدًا إذا كنت تستخدم التحقق الشامل: verifyAll أو verifySequence أو confirmVerified .
val car = mockk< Car >()
every { car.drive( Direction . NORTH ) } returns Outcome . OK
every { car.drive( Direction . SOUTH ) } returns Outcome . OK
excludeRecords { car.drive( Direction . SOUTH ) }
car.drive( Direction . NORTH ) // returns OK
car.drive( Direction . SOUTH ) // returns OK
verify {
car.drive( Direction . NORTH )
}
confirmVerified(car) // car.drive(Direction.SOUTH) was excluded, so confirmation is fine with only car.drive(Direction.NORTH) للتحقق من العمليات المتزامنة ، يمكنك استخدام timeout = xxx :
mockk< MockCls > {
every { sum( 1 , 2 ) } returns 4
Thread {
Thread .sleep( 2000 )
sum( 1 , 2 )
}.start()
verify(timeout = 3000 ) { sum( 1 , 2 ) }
}سينتظر هذا حتى يتم تمرير إحدى حالتين متابعتين: إما تم تمرير التحقق أو الوصول إلى المهلة.
إذا كانت Unit إرجاع وظيفة ، يمكنك استخدام بنية justRun :
class MockedClass {
fun sum ( a : Int , b : Int ): Unit {
println (a + b)
}
}
val obj = mockk< MockedClass >()
justRun { obj.sum(any(), 3 ) }
obj.sum( 1 , 1 )
obj.sum( 1 , 2 )
obj.sum( 1 , 3 )
verify {
obj.sum( 1 , 1 )
obj.sum( 1 , 2 )
obj.sum( 1 , 3 )
} طرق أخرى لكتابة justRun { obj.sum(any(), 3) } :
every { obj.sum(any(), 3) } just Runsevery { obj.sum(any(), 3) } returns Unitevery { obj.sum(any(), 3) } answers { Unit }لتسخين coroutines ، تحتاج إلى إضافة تبعية أخرى إلى مكتبة الدعم.
| Gradle |
|---|
شهادة "org.jetbrains.kotlinx: Kotlinx-coroutines-Core: XX" |
| مافن |
|---|
<premency>
<roupl> org.jetbrains.kotlinx </groupId>
<StifactId> Kotlinx-coroutines-core </stifactid>
<splect> xx </version>
<scope> اختبار </scope>
</التبعية> |
بعد ذلك ، يمكنك استخدام coEvery أو coVerify أو coMatch أو coAssert أو coRun أو coAnswers أو coInvoke إلى وظائف التعليق الوهمية.
val car = mockk< Car >()
coEvery { car.drive( Direction . NORTH ) } returns Outcome . OK
car.drive( Direction . NORTH ) // returns OK
coVerify { car.drive( Direction . NORTH ) } ومحاكاة وظيفة suspend لا تعود أبدًا ، يمكنك استخدام coJustAwait :
runTest {
val car = mockk< Car >()
coJustAwait { car.drive(any()) } // car.drive(...) will never return
val job = launch( UnconfinedTestDispatcher ()) {
car.drive( Direction . NORTH )
}
coVerify { car.drive( Direction . NORTH ) }
job.cancelAndJoin() // Don't forget to cancel the job
}ملاحظة: هناك مشكلة معروفة في حالة استخدام جاسوس مع وظيفة تعليق: #554
يتيح لك Kotlin إعلان الوظائف التي لا تنتمي إلى أي فئة أو كائن ، تسمى وظائف المستوى الأعلى. تتم ترجمة هذه المكالمات إلى طرق ثابتة في بيئات jvm ، ويتم إنشاء فئة Java خاصة للاحتفاظ بالوظائف. يمكن أن تسخر هذه الوظائف ذات المستوى الأعلى باستخدام mockkStatic . تحتاج فقط إلى استيراد الوظيفة وتمرير مرجع كوسيطة:
import com.cars.buildCar
val testCar = Car ()
mockkStatic(::buildCar)
every { buildCar() } returns testCar
assertEquals(testCar, buildCar())
verify { buildCar() } سوف يسخر الدالة أي من النماذج الموجودة في الوظائف الأخرى المعلنة في نفس الملف ، أي ما يعادل استدعاء clearStaticMockk على الفئة المرفقة التي تم إنشاؤها.
هناك ثلاثة أنواع من وظائف التمديد في Kotlin:
بالنسبة لكائن أو فئة ، يمكنك السخرية من وظائف التمديد فقط عن طريق إنشاء mockk العادية:
data class Obj ( val value : Int )
class Ext {
fun Obj. extensionFunc () = value + 5
}
with (mockk< Ext >()) {
every {
Obj ( 5 ).extensionFunc()
} returns 11
assertEquals( 11 , Obj ( 5 ).extensionFunc())
verify {
Obj ( 5 ).extensionFunc()
}
} لسخرية وظائف التمديد على مستوى الوحدة النمطية ، تحتاج إلى إنشاء mockkStatic(...) مع اسم فئة الوحدة النمطية كوسيطة. على سبيل المثال "pkg.filekt" لـ MODULE File.kt في حزمة pkg .
data class Obj ( val value : Int )
// declared in File.kt ("pkg" package)
fun Obj. extensionFunc () = value + 5
mockkStatic( " pkg.FileKt " )
every {
Obj ( 5 ).extensionFunc()
} returns 11
assertEquals( 11 , Obj ( 5 ).extensionFunc())
verify {
Obj ( 5 ).extensionFunc()
} في بيئات jvm ، يمكنك استبدال اسم الفصل مع مرجع الوظيفة:
mockkStatic( Obj ::extensionFunc) لاحظ أن هذا سوف يسخر من فئة pkg.FileKt بالكامل ، وليس فقط extensionFunc .
ينطبق هذا بناء الجملة أيضًا على خصائص التمديد:
val Obj .squareValue get() = value * value
mockkStatic( Obj ::squareValue) إذا تم استخدام @JvmName ، حدده كاسم فئة.
khttp.kt:
@file:JvmName( " KHttp " )
package khttp
// ... KHttp code رمز الاختبار:
mockkStatic( " khttp.KHttp " ) في بعض الأحيان تحتاج إلى معرفة المزيد لسخرية وظيفة التمديد. على سبيل المثال ، classname File.endsWith() وظيفة التمديد.
mockkStatic( " kotlin.io.FilesKt__UtilsKt " )
every { File ( " abc " ).endsWith(any< String >()) } returns true
println ( File ( " abc " ).endsWith( " abc " )) هذا هو سلوك Kotlin القياسي الذي قد لا يمكن التنبؤ به. استخدم Tools -> Kotlin -> Show Kotlin Bytecode أو Check .class Files in JAR Archive للكشف عن مثل هذه الأسماء.
من الإصدار 1.9.1 ، من الممكن معالجة Vararg الممتدة:
interface ClsWithManyMany {
fun manyMany ( vararg x : Any ): Int
}
val obj = mockk< ClsWithManyMany >()
every { obj.manyMany( 5 , 6 , * varargAll { it == 7 }) } returns 3
println (obj.manyMany( 5 , 6 , 7 )) // 3
println (obj.manyMany( 5 , 6 , 7 , 7 )) // 3
println (obj.manyMany( 5 , 6 , 7 , 7 , 7 )) // 3
every { obj.manyMany( 5 , 6 , * anyVararg(), 7 ) } returns 4
println (obj.manyMany( 5 , 6 , 1 , 7 )) // 4
println (obj.manyMany( 5 , 6 , 2 , 3 , 7 )) // 4
println (obj.manyMany( 5 , 6 , 4 , 5 , 6 , 7 )) // 4
every { obj.manyMany( 5 , 6 , * varargAny { nArgs > 5 }, 7 ) } returns 5
println (obj.manyMany( 5 , 6 , 4 , 5 , 6 , 7 )) // 5
println (obj.manyMany( 5 , 6 , 4 , 5 , 6 , 7 , 7 )) // 5
every {
obj.manyMany( 5 , 6 , * varargAny {
if (position < 3 ) it == 3 else it == 4
}, 7 )
} returns 6
println (obj.manyMany( 5 , 6 , 3 , 4 , 7 )) // 6
println (obj.manyMany( 5 , 6 , 3 , 4 , 4 , 7 )) // 6إذا كنت بحاجة إلى السخرية من الوظائف الخاصة ، فيمكنك القيام بذلك عبر مكالمة ديناميكية.
class Car {
fun drive () = accelerate()
private fun accelerate () = " going faster "
}
val mock = spyk< Car >(recordPrivateCalls = true )
every { mock[ " accelerate " ]() } returns " going not so fast "
assertEquals( " going not so fast " , mock.drive())
verifySequence {
mock.drive()
mock[ " accelerate " ]()
} إذا كنت ترغب في التحقق من المكالمات الخاصة ، فيجب عليك إنشاء spyk مع recordPrivateCalls = true
بالإضافة إلى ذلك ، يتيح لك بناء جملة أكثر مطولًا الحصول على خصائص وضبطها ، مع نفس المكالمات الديناميكية:
val mock = spyk( Team (), recordPrivateCalls = true )
every { mock getProperty " speed " } returns 33
every { mock setProperty " acceleration " value less( 5 ) } just runs
justRun { mock invokeNoArgs " privateMethod " }
every { mock invoke " openDoor " withArguments listOf ( " left " , " rear " ) } returns " OK "
verify { mock getProperty " speed " }
verify { mock setProperty " acceleration " value less( 5 ) }
verify { mock invoke " openDoor " withArguments listOf ( " left " , " rear " ) } يمكنك الوصول إلى حقول الدعم عبر fieldValue واستخدام value للقيمة التي يتم تعيينها.
ملاحظة: في الأمثلة أدناه ، نستخدم propertyType لتحديد نوع fieldValue . هذا مطلوب لأنه من الممكن التقاط النوع تلقائيًا لـ Getter. استخدام nullablePropertyType لتحديد نوع لاغية.
val mock = spyk( MockCls (), recordPrivateCalls = true )
every { mock.property } answers { fieldValue + 6 }
every { mock.property = any() } propertyType Int :: class answers { fieldValue + = value }
every { mock getProperty " property " } propertyType Int :: class answers { fieldValue + 6 }
every { mock setProperty " property " value any< Int >() } propertyType Int :: class answers { fieldValue + = value }
every {
mock.property = any()
} propertyType Int :: class answers {
fieldValue = value + 1
} andThen {
fieldValue = value - 1
}إضافة سلوكيات إضافية عبر الواجهات وتبادلها:
val spy = spyk( System . out , moreInterfaces = arrayOf( Runnable :: class ))
spy. println ( 555 )
every {
(spy as Runnable ). run ()
} answers {
(self as PrintStream ). println ( " Run! Run! Run! " )
}
val thread = Thread (spy as Runnable )
thread.start()
thread.join() لا شيء مميز هنا. إذا كان لديك وظيفة لا تعيد Nothing :
fun quit ( status : Int ): Nothing {
exitProcess(status)
}ثم يمكنك على سبيل المثال رمي استثناء كسلوك:
every { quit( 1 ) } throws Exception ( " this is a test " ) وهناك وهمية من النطاق هي نفسية تلقائيًا تلقائيًا بعد تنفيذ كتلة التعليمات البرمجية كمعلمة. يمكنك استخدام وظائف mockkObject و mockkStatic و mockkConstructor .
object ObjBeingMocked {
fun add ( a : Int , b : Int ) = a + b
}
// ObjBeingMocked will be unmocked after this scope
mockkObject( ObjBeingMocked ) {
assertEquals( 3 , ObjBeingMocked .add( 1 , 2 ))
every { ObjBeingMocked .add( 1 , 2 ) } returns 55
assertEquals( 55 , ObjBeingMocked .add( 1 , 2 ))
} هناك طريقة بسيطة للغاية لإنشاء مطابقات جديدة هي إرفاق وظيفة إلى MockKMatcherScope أو MockKVerificationScope واستخدام وظيفة match :
fun MockKMatcherScope. seqEq ( seq : Sequence < String >) = match< Sequence < String >> {
it.toList() == seq.toList()
} من الممكن أيضًا إنشاء المزيد من المطاعم المتقدمة من خلال تطبيق واجهة Matcher .
مثال على المطابقة المخصصة التي تقارن القائمة دون ترتيب:
@Test
fun test () {
class MockCls {
fun op ( a : List < Int >) = a.reversed()
}
val mock = mockk< MockCls >()
every { mock.op(any()) } returns listOf ( 5 , 6 , 9 )
println (mock.op( listOf ( 1 , 2 , 3 )))
verify { mock.op(matchListWithoutOrder( 3 , 2 , 1 )) }
}
data class ListWithoutOrderMatcher < T >(
val expectedList : List < T >,
val refEq : Boolean
) : Matcher<List<T>> {
val map = buildCountsMap(expectedList, refEq)
override fun match ( arg : List < T > ? ): Boolean {
if (arg == null ) return false
return buildCountsMap(arg, refEq) == map
}
private fun buildCountsMap ( list : List < T >, ref : Boolean ): Map < Any ?, Int > {
val map = mutableMapOf< Any ?, Int >()
for (item in list) {
val key = when {
item == null -> nullKey
refEq -> InternalPlatform .ref(item)
else -> item
}
map.compute(key, { _, value -> (value ? : 0 ) + 1 })
}
return map
}
override fun toString () = " matchListWithoutOrder( $expectedList ) "
@Suppress( " UNCHECKED_CAST " )
override fun substitute ( map : Map < Any , Any >): Matcher < List < T >> {
return copy(expectedList = expectedList.map { map.getOrDefault(it as Any? , it) } as List < T >)
}
companion object {
val nullKey = Any ()
}
}
inline fun < reified T : List < E >, E : Any > MockKMatcherScope. matchListWithoutOrder (
vararg items : E ,
refEq : Boolean = true
): T = match( ListWithoutOrderMatcher ( listOf ( * items), refEq))مثال باستخدام انعكاس لسخر جميع الطرق على كائن على طراز البناء
val builderFunctions = MyBuilder :: class .memberFunctions.filter { it.returnType.classifier == MyBuilder :: class }
val builderMock = mockk< MyBuilder > {
builderFunctions.forEach { func ->
every {
val params = listOf< Any ?>(builderMock) + func.parameters.drop( 1 ).map { any(it.type.classifier as KClass < Any >) }
func.call( * params.toTypedArray())
} answers {
this @mockk
}
}
}لضبط المعلمات على مستوى العالم ، هناك بعض الإعدادات التي يمكنك تحديدها في ملف مورد.
كيفية استخدام:
io/mockk/settings.properties في src/main/resources . relaxed =true|false
relaxUnitFun =true|false
recordPrivateCalls =true|false
stackTracesOnVerify =true|false
stackTracesAlignment =left|center تحدد stackTracesAlignment ما إذا كان سيتم محاذاة آثار المكدس إلى المركز (افتراضي) ، أو إلى اليسار (أكثر اتساقًا مع JVM Stacktraces).
فيما يلي بعض الجداول لمساعدتك في إتقان DSL.
| وظيفة | وصف |
|---|---|
mockk<T>(...) | يبني وهمية منتظمة |
spyk<T>() | يبني جاسوس باستخدام المُنشئ الافتراضي |
spyk(obj) | يبني جاسوس عن طريق النسخ من obj |
slot | يخلق فتحة التقاط |
every | يبدأ كتلة القطع |
coEvery | يبدأ كتلة قادة ل Coroutines |
verify | يبدأ كتلة التحقق |
coVerify | يبدأ كتلة التحقق من coroutines |
verifyAll | تبدأ كتلة التحقق التي يجب أن تتضمن جميع المكالمات |
coVerifyAll | تبدأ كتلة التحقق التي يجب أن تتضمن جميع المكالمات لـ Coroutines |
verifyOrder | تبدأ كتلة التحقق التي تتحقق من الطلب |
coVerifyOrder | تبدأ كتلة التحقق التي تتحقق من ترتيب coroutines |
verifySequence | تبدأ كتلة التحقق التي تتحقق مما إذا كانت جميع المكالمات قد تم إجراء في تسلسل محدد |
coVerifySequence | تبدأ كتلة التحقق التي تتحقق مما إذا كانت جميع المكالمات قد أجريت في تسلسل محدد لـ Coroutines |
excludeRecords | استبعاد بعض المكالمات من التسجيل |
confirmVerified | يؤكد أنه تم التحقق من جميع المكالمات المسجلة |
checkUnnecessaryStub | يؤكد أن جميع المكالمات المسجلة تستخدم مرة واحدة على الأقل |
clearMocks | يمسح السخرية المحددة |
registerInstanceFactory | يتيح لك إعادة تعريف طريقة إنشاء كائن معين |
mockkClass | يبني وهمية منتظمة عن طريق تمرير الفصل كمعلمة |
mockkObject | يحول كائن إلى كائن وهمي ، أو يمسحه إذا تم تحويله بالفعل |
unmockkObject | يعيد كائن يسخر إلى كائن منتظم |
mockkStatic | يجعل السخرية الثابتة من فئة ما ، أو مسحها إذا تم تحويلها بالفعل |
unmockkStatic | يعيد وهمية ثابتة إلى فئة منتظمة |
clearStaticMockk | يمسح وهمية ثابتة |
mockkConstructor | يجعل مُنشئًا يسخر من الفصل ، أو يمسحه إذا تم تحويله بالفعل |
unmockkConstructor | يحول عودة مُنشئ إلى فئة منتظمة |
clearConstructorMockk | يمس |
unmockkAll | كائن unmocks ، يسخر من الساكنة والمنشأة |
clearAllMocks | يمسح السخرية العادية والكائن والثابت والمشارك |
بشكل افتراضي ، تتم مطابقة الوسائط البسيطة باستخدام eq()
| مطابقة | وصف |
|---|---|
any() | يطابق أي حجة |
any(Class) | يطابق أي حجة من فئة العطاء (للسخرية العاكسة) |
allAny() | مطابقة خاصة تستخدم any() بدلاً من eq() للمطابقة التي يتم توفيرها كوسائط بسيطة |
isNull() | يتحقق إذا كانت القيمة لاغية |
isNull(inverse=true) | يتحقق ما إذا كانت القيمة ليست فارغة |
ofType(type) | يتحقق ما إذا كانت القيمة تنتمي إلى النوع |
match { it.startsWith("string") } | المباريات عبر المسند الذي تم تمريره |
coMatch { it.startsWith("string") } | تطابق عبر مسند كوروتين الذي تم تمريره |
matchNullable { it?.startsWith("string") } | تطابق قيمة لاغية عبر المسند الذي تم تمريره |
coMatchNullable { it?.startsWith("string") } | تطابق قيمة لاغية عبر مسند كوروتين الذي تم تمريره |
eq(value) | تتطابق إذا كانت القيمة مساوية للقيمة المقدمة من خلال وظيفة deepEquals |
eq(value, inverse=true) | تطابق إذا كانت القيمة لا تساوي القيمة المقدمة عبر وظيفة deepEquals |
neq(value) | تطابق إذا كانت القيمة لا تساوي القيمة المقدمة عبر وظيفة deepEquals |
refEq(value) | تطابق إذا كانت القيمة مساوية للقيمة المقدمة من خلال مقارنة المرجع |
refEq(value, inverse=true) | تتطابق إذا كانت القيمة لا تساوي القيمة المقدمة عن طريق المقارنة المرجعية |
nrefEq(value) | تتطابق إذا كانت القيمة لا تساوي القيمة المقدمة عن طريق المقارنة المرجعية |
cmpEq(value) | تتطابق إذا كانت القيمة مساوية للقيمة المقدمة عبر وظيفة compareTo |
less(value) | تتطابق إذا كانت القيمة أقل من القيمة المقدمة عبر وظيفة compareTo |
more(value) | تتطابق إذا كانت القيمة أكثر من القيمة المقدمة عبر وظيفة compareTo |
less(value, andEquals=true) | تتطابق إذا كانت القيمة أقل من أو تساوي القيمة المقدمة عبر وظيفة compareTo |
more(value, andEquals=true) | تتطابق إذا كانت القيمة أكثر من أو تساوي القيمة المقدمة عبر وظيفة compareTo |
range(from, to, fromInclusive=true, toInclusive=true) | تتطابق إذا كانت القيمة في المدى عبر وظيفة compareTo |
and(left, right) | يجمع بين اثنين من المطاعم عبر منطقية و |
or(left, right) | يجمع بين اثنين من المطاعم عبر منطق أو |
not(matcher) | ينفي المطابقة |
capture(slot) | يلتقط قيمة غير قابلة للإلغاء إلى CapturingSlot |
captureNullable(slot) | يلتقط قيمة لاغية إلى CapturingSlot |
capture(mutableList) | يلتقط قيمة إلى قائمة |
captureNullable(mutableList) | يلتقط قيمة إلى قائمة مع القيم الخالية |
captureLambda() | يلتقط لامدا |
captureCoroutine() | يلتقط coroutine |
invoke(...) | يدعو حجة متطابقة |
coInvoke(...) | يدعو حجة متطابقة إلى coroutine |
hint(cls) | يلمح نوع الإرجاع التالي في حالة محوها |
anyVararg() | يطابق أي عناصر في فارارج |
varargAny(matcher) | تطابق إذا كان أي عنصر يطابق المطابقة |
varargAll(matcher) | تطابق إذا كانت جميع العناصر تتطابق مع المطابقة |
any...Vararg() | يطابق أي عناصر في Vararg (خاصة بالنوع البدائي) |
varargAny...(matcher) | تطابق إذا كان أي عنصر يطابق المطابقة (خاصة بالنوع البدائي) |
varargAll...(matcher) | تطابق إذا كانت جميع العناصر تتطابق مع المطابقة (خاصة بالنوع البدائي) |
عدد قليل من المطاعم الخاصة المتاحة في وضع التحقق فقط:
| مطابقة | وصف |
|---|---|
withArg { code } | يطابق أي قيمة ويسمح بتنفيذ بعض التعليمات البرمجية |
withNullableArg { code } | يطابق أي قيمة لاغية ويسمح بتنفيذ بعض التعليمات البرمجية |
coWithArg { code } | يطابق أي قيمة ويسمح بتنفيذ بعض رمز coroutine |
coWithNullableArg { code } | يطابق أي قيمة لاغية ويسمح بتنفيذ بعض رمز coroutine |
| المدقق | وصف |
|---|---|
verify { mock.call() } | قم بالتحقق غير المرتبط بأن مكالمة تم إجراؤها |
verify(inverse=true) { mock.call() } | قم بالتحقق غير المرتب أنه لم يتم إجراء مكالمة |
verify(atLeast=n) { mock.call() } | قم بالتحقق غير المرتبط بأن مكالمة تم إجراؤها على الأقل n مرات |
verify(atMost=n) { mock.call() } | قم بالتحقق غير المرتب أنه تم إجراء مكالمة على معظم n |
verify(exactly=n) { mock.call() } | قم بالتحقق غير المرتبط بأن مكالمة تم إجراؤها n |
verifyAll { mock.call1(); mock.call2() } | قم بالتحقق غير المرتبط بأن المكالمات المحددة فقط تم تنفيذها للسخرية المذكورة |
verifyOrder { mock.call1(); mock.call2() } | تحقق من أن تسلسل المكالمات ذهب واحدًا تلو الآخر |
verifySequence { mock.call1(); mock.call2() } | قم بالتحقق من أنه تم تنفيذ التسلسل المحدد للمكالمات فقط للأسعار المذكورة |
verify { mock wasNot Called } | هل تحقق من أن وهمية لم يتم استدعاؤها |
verify { listOf(mock1, mock2) wasNot Called } | هل تحقق من أن قائمة السخرية لم يتم استدعاؤها |
يمكن متابعة إجابة إجابة إضافية أو أكثر.
| إجابة | وصف |
|---|---|
returns value | حدد أن المكالمة المتطابقة تُرجع قيمة محددة |
returnsMany list | حدد أن المكالمة المتطابقة تُرجع قيمة من القائمة ، مع إرجاع المكالمات اللاحقة العنصر التالي |
returnsArgument(n) | حدد أن المكالمة المتطابقة تُرجع الوسيطة التاسعة لتلك المكالمة |
throws ex | حدد أن المكالمة المتطابقة ترمي استثناء |
throwsMany list | حدد أن المكالمات المتطابقة ترمي استثناء من القائمة ، مع استثناء المكالمات اللاحقة الاستثناء التالي |
answers { code } | حدد أن إجابات المكالمات المتطابقة مع كتلة رمز مع answer scope |
coAnswers { code } | حدد أن إجابات المكالمات المتطابقة مع كتلة رمز coroutine مع answer scope |
answers answerObj | حدد أن إجابات المكالمات المتطابقة مع كائن إجابة |
answers { nothing } | حدد أن إجابات المكالمات المتطابقة خالية |
just Runs | حدد أن المكالمة المتطابقة هي وحدة إرجاع (إرجاع لاغية) |
just Awaits | حدد أن المكالمة المتطابقة لا تُرجع أبدًا (متوفرة منذ الإصدار 1.13.3) |
propertyType Class | حدد نوع ملحق حقل الدعم |
nullablePropertyType Class | حدد نوع ملحق حقل الدعم كنوع لاغ |
يتم إرجاع الإجابة التالية على كل مكالمة ناتجة ويتم استمرار القيمة الأخيرة. لذلك هذا يشبه دلالات returnsMany .
| إجابة إضافية | وصف |
|---|---|
andThen value | حدد أن المكالمة المتطابقة تُرجع قيمة محددة |
andThenMany list | حدد أن المكالمة المتطابقة تُرجع قيمة من القائمة ، مع إرجاع المكالمات اللاحقة العنصر التالي |
andThenThrows ex | حدد أن المكالمة المتطابقة ترمي استثناء |
andThenThrowsMany ex | حدد أن المكالمات المتطابقة ترمي استثناء من القائمة ، مع استثناء المكالمات اللاحقة الاستثناء التالي |
andThen { code } | حدد أن إجابات المكالمات المتطابقة مع كتلة رمز مع answer scope |
coAndThen { code } | حدد أن إجابات المكالمات المتطابقة مع كتلة رمز coroutine مع answer scope |
andThenAnswer answerObj | حدد أن إجابات المكالمات المتطابقة مع كائن إجابة |
andThen { nothing } | حدد أن إجابات المكالمات المتطابقة خالية |
andThenJust Runs | حدد أن المكالمة المتطابقة هي وحدة إرجاع (متوفرة منذ V1.12.2) |
andThenJust Awaits | حدد أن المكالمة المتطابقة لا تُعود أبدًا (متوفرة منذ الإصدار 1.13.3) |
| المعلمة | وصف |
|---|---|
call | كائن استدعاء يتكون من الاحتجاج والمطابقة |
invocation | يحتوي على معلومات تتعلق بالوظيفة الفعلية التي تم الاحتجاج بها |
matcher | يحتوي على معلومات تتعلق بالمطابقة المستخدمة لمطابقة الاحتجاج |
self | الإشارة إلى استدعاء الكائن الذي تم إجراؤه |
method | الإشارة إلى استدعاء الوظيفة التي تم إجراؤها |
args | الإشارة إلى وسيطات الاحتجاج |
nArgs | عدد حجج الاحتجاج |
arg(n) | الحجة التاسعة |
firstArg() | الحجة الأولى |
secondArg() | الحجة الثانية |
thirdArg() | الحجة الثالثة |
lastArg() | الحجة الأخيرة |
captured() | العنصر الأخير في قائمة الراحة عند التقاط قائمة |
lambda<...>().invoke() | اتصل بـ Lambda الملتقط |
coroutine<...>().coInvoke() | اتصل بـ coroutine الملتقط |
nothing | قيمة فارغة لإرجاع nothing كإجابة |
fieldValue | ملحق إلى مجال دعم العقار |
fieldValueAny | ملحق إلى حقل دعم العقار مع Any? يكتب |
value | القيمة التي يتم تعيينها ، تم تصويرها على نفس النوع مثل حقل دعم الممتلكات |
valueAny | القيمة التي يتم تعيينها ، مع Any? يكتب |
callOriginal | يستدعي الوظيفة الأصلية |
| المعلمة | وصف |
|---|---|
position | موضع حجة في صفيف فارارج |
nArgs | العدد العام للحجج في صفيف فارارج |
يمكنك أيضًا دعم هذا المشروع من خلال أن تصبح راعياً. سيظهر شعارك هنا مع رابط لموقع الويب الخاص بك.
شكرا لجميع مؤيدينا!
هذا المشروع موجود بفضل جميع الأشخاص الذين يساهمون.
لطرح الأسئلة ، يرجى استخدام Stack Overflow أو Gitter.
للإبلاغ عن الأخطاء ، يرجى استخدام مشروع GitHub.