시작해야 할 것은 MockK 라이브러리에 의존성을 추가하는 것입니다.
| 접근하다 | 지침 |
|---|---|
![]() | 증언 "io.mockk : mockk : $ {mockkversion}"
|
(Kotlin DSL) | 증언 ( "io.mockk : mockk : $ {mockkversion}")) |
![]() | <의존성>
<groupid> io.mockk </groupid>
<artifactid> mockk-jvm </artifactid>
<버전> $ {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를 설명합니다.
기초
예상되는 행동 및 행동 검증
Mockk 기능
Mockk 고급 기능
Kotlin, Junit 및 Mockk로 쿼크를 테스트합니다
Mockk 's Black Magic을 풀고 (en, 번역)
Mockk 가이드 북
Marco Cattaneo의“Mockk를 사용한 Kotlin Unit Testing”
(비디오) mockk에서 검증을 사용하여 Mocked Object에서 함수 호출을 확인하십시오.
Raywenderlich.com에서 Mockk 유료 코스 테스트
안드로이드 비디오 튜토리얼 파트 1, 2 부의 Ryan Kay 용 TDD
(비디오) Android 개발자 라이브 코딩 #13 : Mockk, Coroutines, 테스트 중심 개발로 단위 테스트
Kotlinconf 2018- Philipp Hauer의 Kotlin에서 단위 테스트를위한 모범 사례
Kotlin-Fullstack-Sample은 테스트로 덮인 Mockk 프로젝트를 사용합니다
DZONE 기사
Habrahabr 기사 (RU)
Mockk -Yannick de Turck과 함께 Kotlin에서 조롱
주석을 사용하여 모의 개체 생성을 단순화 할 수 있습니다.
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
}
} 주입은 먼저 속성을 이름으로, 클래스 또는 슈퍼 클래스별로 일치 시키려고합니다. Customization의 lookupType 매개 변수를 확인하십시오.
private 적용 되더라도 속성이 주입됩니다. 주입을위한 생성자는 가장 큰 수의 인수에서 최저로 선택됩니다.
@InjectMockKs 기본적으로 지정되지 않은 lateinit var 또는 var 만 주입합니다. 이를 변경하려면 overrideValues = true 사용하십시오. 이것은 이미 초기화 된 경우에도 값을 할당합니다. val 을 주입하려면 injectImmutable = true 사용하십시오. 더 짧은 표기법의 경우 기본적으로 @InjectMockKs 와 동일한 @OverrideMockKs 를 사용하지만이 두 플래그를 켭니다.
Junit 4는 규칙 기반 API를 노출하여 테스트 라이프 사이클 후 일부 자동화를 허용합니다. 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
} 마지막으로,이 확장은 @AfterAll 콜백에서 unmockkAll 및 clearAllMocks 호출하여 각 테스트 클래스 실행 후 테스트 환경이 깨끗해 지도록합니다. mockk.junit.extension.keepmocks=true Property를 설정하여 @MockKExtension.KeepMocks 주석을 클래스 또는 전 세계에 추가 하여이 동작을 비활성화 할 수 있습니다. (v1.13.11이기 때문에) 또는 기본적으로 clearAllMocks ( currentThreadOnly=false )가 스레드-안전하지 않기 때문에, 테스트를 병렬로 실행 해야하는 경우 MockKExtension.RequireParallelTesting mockk.junit.extension.requireParallelTesting=true 추가 할 수 @AfterAll . clearAllMocks 명시 적으로 호출되면 clearAllMocks(currentThreadOnly = true) 제공하여 동일한 스레드 내에서 생성 된 모의 만 제거 할 수 있습니다 (v1.13.12 이후).
@MockKExtension.ConfirmVerification 과 테스트 클래스에 주석을 달면서 모든 스터브 된 메소드가 실제로 검증 될 수 있습니다.
이는 각 테스트 후 모든 모의에 confirmVerified 내부적으로 호출하여 불필요한 완비가 없는지 확인합니다.
이 동작은 IDE에서 테스트를 실행할 때 예상대로 작동하지 않을 수 있습니다.이 confirmVerified 통화가 실패 할 때 발생하는 예외를 처리하는 Gradle이기 때문입니다.
@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 -반발 함수를 편안하게하려면 mockk 함수, @MockK 주석 또는 MockKAnnotations.init 함수에 대한 인수로 relaxUnitFun = true 사용할 수 있습니다.
기능:
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 Langu
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 된다는 것입니다.
이러한 모의의 조롱 거동은 anyConstructed<MockCls>() 로 표시된 특수 prototype mock 에 연결됩니다.
이러한 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 )
} 이 경우, constructedWith 된 모든 인수 마칭 자 세트에 대해 prototype mock 만들어졌습니다.
정기적 인 인수와 매칭자를 모두 혼합 할 수 있습니다.
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 일반적으로 Factory Method 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... complects에 의해 확인되었다는 것을 두 번 확인하려면 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 }코 루틴을 조롱하려면 지원 라이브러리에 다른 종속성을 추가해야합니다.
| Gradle |
|---|
증언 "org.jetbrains.kotlinx : kotlinx-coroutines-core : xx" |
| Maven |
|---|
<의존성>
<groupid> org.jetbrains.kotlinx </groupid>
<artifactid> Kotlinx-coroutines-core </artifactid>
<버전> xx </version>
<Scope> 테스트 </scope>
</의존성> |
그런 다음 coEvery , coVerify , coMatch , coAssert , coRun , coAnswers 또는 coInvoke 사용하여 Mock Spend 기능을 수행 할 수 있습니다.
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() } 함수를 조롱하면 생성 된 Enclosing 클래스에서 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 패키지의 모듈 File.kt 에 대한 "pkg.filekt"
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) 이것은 extensionFunc 뿐만 아니라 전체 pkg.FileKt 클래스를 조롱합니다.
이 구문은 확장 속성에도 적용됩니다.
val Obj .squareValue get() = value * value
mockkStatic( Obj ::squareValue) @JvmName 사용하는 경우 클래스 이름으로 지정하십시오.
khttp.kt :
@file:JvmName( " KHttp " )
package khttp
// ... KHttp code 테스트 코드 :
mockkStatic( " khttp.KHttp " ) 때로는 확장 기능을 조롱하려면 조금 더 알아야합니다. 예를 들어 확장 기능 File.endsWith() 완전히 예측할 수없는 classname 가지고 있습니다.
mockkStatic( " kotlin.io.FilesKt__UtilsKt " )
every { File ( " abc " ).endsWith(any< String >()) } returns true
println ( File ( " abc " ).endsWith( " abc " )) 이것은 예측할 수없는 표준 Kotlin 동작입니다. Tools -> Kotlin -> Show Kotlin Bytecode 또는 JAR Archive의 .class 파일을 확인하여 이러한 이름을 감지하십시오.
버전 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 " ]()
} 개인 통화를 확인하려면 recordPrivateCalls = true 로 spyk 만들어야합니다.
또한, 더 장점 구문을 사용하면 동일한 동적 통화와 결합 된 속성을 얻고 설정할 수 있습니다.
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 사용하여 Nullable 유형을 지정하십시오.
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
}
}
}전 세계적으로 매개 변수를 조정하려면 리소스 파일에 지정할 수있는 몇 가지 설정이 있습니다.
사용 방법 :
src/main/resources 에서 io/mockk/settings.properties 파일을 만듭니다. relaxed =true|false
relaxUnitFun =true|false
recordPrivateCalls =true|false
stackTracesOnVerify =true|false
stackTracesAlignment =left|center stackTracesAlignment 스택 트레이스를 중앙 (기본값) 또는 왼쪽 (일반적인 JVM 스택 트레이스와 더 일치)에 정렬할지 여부를 결정합니다.
다음은 DSL을 마스터하는 데 도움이되는 몇 가지 테이블입니다.
| 기능 | 설명 |
|---|---|
mockk<T>(...) | 정기적 인 모의를 만듭니다 |
spyk<T>() | 기본 생성자를 사용하여 스파이를 구축합니다 |
spyk(obj) | obj 에서 복사하여 스파이를 구축합니다 |
slot | 캡처 슬롯을 만듭니다 |
every | 스터브 블록을 시작합니다 |
coEvery | 코 루틴 용 스터브 블록을 시작합니다 |
verify | 확인 블록을 시작합니다 |
coVerify | 코 루틴에 대한 검증 블록을 시작합니다 |
verifyAll | 모든 통화를 포함 해야하는 확인 블록을 시작합니다 |
coVerifyAll | 코 루틴에 대한 모든 호출을 포함 해야하는 확인 블록을 시작합니다. |
verifyOrder | 주문을 확인하는 확인 블록을 시작합니다 |
coVerifyOrder | 코 루틴 주문을 확인하는 확인 블록을 시작합니다. |
verifySequence | 모든 통화가 지정된 시퀀스로 이루어 졌는지 확인하는 확인 블록을 시작합니다. |
coVerifySequence | 모든 통화가 Coroutines에 지정된 시퀀스로 이루어 졌는지 확인하는 확인 블록을 시작합니다. |
excludeRecords | 일부 통화가 기록되지 않도록 제외하십시오 |
confirmVerified | 기록 된 모든 통화가 확인되었는지 확인합니다 |
checkUnnecessaryStub | 기록 된 모든 통화가 적어도 한 번은 사용되는지 확인합니다. |
clearMocks | 지정된 모의를 지 웁니다 |
registerInstanceFactory | 특정 개체의 인스턴스화 방식을 재정의 할 수 있습니다. |
mockkClass | 클래스를 매개 변수로 전달하여 일반 모의를 구축합니다. |
mockkObject | 객체를 물체 모의로 바꾸거나 이미 변환 된 경우 지우칩니다. |
unmockkObject | 객체 모의를 다시 일반 객체로 돌립니다 |
mockkStatic | 수업에서 정적 모의를 만들거나 이미 변형 된 경우 지우칩니다. |
unmockkStatic | 정적 모의를 다시 정규 수업으로 바꿉니다 |
clearStaticMockk | 정적 모의를 지 웁니다 |
mockkConstructor | 생성자가 클래스에서 모킹을 만들거나 이미 변형 된 경우 지우칩니다. |
unmockkConstructor | 생성자 모의를 정규 클래스로 다시 돌립니다 |
clearConstructorMockk | 생성자 모의를 지 웁니다 |
unmockkAll | 모킹 객체, 정적 및 생성자 모의 |
clearAllMocks | 규칙, 물체, 정적 및 생성자 모의를 지 웁니다 |
기본적으로 간단한 인수는 eq() 사용하여 일치합니다.
| 경기자 | 설명 |
|---|---|
any() | 모든 주장과 일치합니다 |
any(Class) | 주는 클래스의 주장과 일치합니다 (반사 조롱을 위해) |
allAny() | 간단한 인수로 제공되는 매치자를 위해 eq any() 대신 eq() 를 사용하는 특수 매치기 |
isNull() | 값이 무효인지 확인합니다 |
isNull(inverse=true) | 값이 널이 아닌지 확인합니다 |
ofType(type) | 값이 유형에 속하는지 확인합니다 |
match { it.startsWith("string") } | 통과 된 술어를 통한 일치 |
coMatch { it.startsWith("string") } | 통과 된 코 루틴 술어를 통한 일치 |
matchNullable { it?.startsWith("string") } | 전달 된 술어를 통해 무효 값과 일치합니다 |
coMatchNullable { it?.startsWith("string") } | 통과 된 Coroutine 술어를 통해 무효 값과 일치합니다 |
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() | 코 루틴을 캡처합니다 |
invoke(...) | 일치하는 인수를 부릅니다 |
coInvoke(...) | 코 루틴에 대해 일치하는 인수를 호출합니다 |
hint(cls) | 지워진 경우 다음 반환 유형을 힌트합니다. |
anyVararg() | Vararg의 모든 요소와 일치합니다 |
varargAny(matcher) | 요소가 매치 자와 일치하는 경우 일치합니다 |
varargAll(matcher) | 일치 모든 요소가 매칭 자와 일치하는 경우 |
any...Vararg() | Vararg의 모든 요소와 일치합니다 (원시 유형에 따라) |
varargAny...(matcher) | 일치하는 요소가 매칭 (원시 유형에 따라)과 일치하는 경우 일치합니다. |
varargAll...(matcher) | 일치하는 모든 요소가 매칭 자와 일치하는 경우 (원시 유형에 따라) |
검증 모드에서만 사용할 수있는 몇 개의 특수 매치 자.
| 경기자 | 설명 |
|---|---|
withArg { code } | 모든 값과 일치하고 일부 코드를 실행할 수 있습니다 |
withNullableArg { code } | 널리킹 불가능한 값과 일치하고 일부 코드를 실행할 수 있습니다. |
coWithArg { code } | 모든 값과 일치하고 일부 코 루틴 코드를 실행할 수 있습니다. |
coWithNullableArg { code } | 무효 값과 일치하고 일부 코 루틴 코드를 실행할 수 있습니다. |
| 유효성 검사기 | 설명 |
|---|---|
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) | 일치하는 통화가 해당 호출의 N 번째 인수를 반환하도록 지정합니다. |
throws ex | 일치하는 통화가 예외를 던지도록 지정하십시오 |
throwsMany list | 일치하는 통화가 목록에서 예외를 던지고 후속 통화가 다음 예외를 던지도록 지정합니다. |
answers { code } | answer scope 로 스코핑 된 코드 블록으로 일치하는 통화 답변을 지정하십시오. |
coAnswers { code } | answer scope 가있는 코 루틴 코드 블록으로 일치하는 통화 답변을 지정하십시오. |
answers answerObj | 응답 개체와 일치하는 통화 답변을 지정하십시오. |
answers { nothing } | 일치하는 통화 대답이 null을 지정하십시오 |
just Runs | 일치하는 통화가 반환 장치임을 지정합니다 (반환 NULL) |
just Awaits | 일치하는 통화가 반환되지 않도록 지정하십시오 (v1.13.3 이후 사용 가능). |
propertyType Class | 백킹 필드 액세서의 유형을 지정하십시오 |
nullablePropertyType Class | 백킹 필드 액세서의 유형을 무효 유형으로 지정합니다. |
각 결과 호출에 대한 다음 답변이 반환되고 마지막 값은 지속됩니다. 따라서 이것은 returnsMany Semantics와 유사합니다.
| 추가 답변 | 설명 |
|---|---|
andThen value | 일치하는 통화가 하나의 지정된 값을 반환하도록 지정하십시오 |
andThenMany list | 일치하는 통화가 목록에서 값을 반환하도록 지정하고 후속 통화는 다음 요소를 반환합니다. |
andThenThrows ex | 일치하는 통화가 예외를 던지도록 지정하십시오 |
andThenThrowsMany ex | 일치하는 통화가 목록에서 예외를 던지고 후속 통화가 다음 예외를 던지도록 지정하십시오. |
andThen { code } | answer scope 로 스코핑 된 코드 블록으로 일치하는 통화 답변을 지정하십시오. |
coAndThen { code } | answer scope 가있는 코 루틴 코드 블록으로 일치하는 통화 답변을 지정하십시오. |
andThenAnswer answerObj | 응답 개체와 일치하는 통화 답변을 지정하십시오. |
andThen { nothing } | 일치하는 통화 대답이 null을 지정하십시오 |
andThenJust Runs | 일치하는 통화가 반환 장치임을 지정합니다 (v1.12.2 이후 사용 가능). |
andThenJust Awaits | 일치하는 통화가 반환되지 않음을 지정하십시오 (v1.13.3 이후 사용 가능). |
| 매개 변수 | 설명 |
|---|---|
call | 호출 및 매칭으로 구성된 통화 객체 |
invocation | 호출 된 실제 기능에 관한 정보가 포함되어 있습니다 |
matcher | 호출과 일치하는 데 사용되는 매칭에 관한 정보가 포함되어 있습니다. |
self | 객체 호출에 대한 참조 |
method | 기능 호출에 대한 참조 |
args | 호출 인수에 대한 언급 |
nArgs | 호출 인수의 수 |
arg(n) | n 번째 논쟁 |
firstArg() | 첫 번째 논쟁 |
secondArg() | 두 번째 논쟁 |
thirdArg() | 세 번째 주장 |
lastArg() | 마지막 주장 |
captured() | 목록에 캡처 할 때 편의를위한 목록의 마지막 요소 |
lambda<...>().invoke() | 포획 된 람다에게 전화하십시오 |
coroutine<...>().coInvoke() | 캡처 된 코 루틴에 전화하십시오 |
nothing | 답으로 nothing 반환 할 수있는 널 가치 |
fieldValue | 부동산 후원 필드에 대한 액세서 |
fieldValueAny | 부동산 후원 필드에 대한 액세서가 있습니까 Any? 유형 |
value | 설정중인 값, 속성 백킹 필드와 동일한 유형으로 캐스트 |
valueAny | 값이 설정되고 있습니까 Any? 유형 |
callOriginal | 원래 함수를 호출합니다 |
| 매개 변수 | 설명 |
|---|---|
position | vararg 배열에서 인수의 위치 |
nArgs | vararg 배열의 전체 인수 수 |
스폰서가 되어이 프로젝트를 지원할 수도 있습니다. 귀하의 로고는 귀하의 웹 사이트 링크와 함께 여기에 표시됩니다.
우리의 모든 후원자들에게 감사합니다!
이 프로젝트는 기부 한 모든 사람들 덕분에 존재합니다.
질문을하려면 스택 오버플로 또는 gitter를 사용하십시오.
버그를보고하려면 Github 프로젝트를 사용하십시오.