Unit tesztels Swiftben Clkeresztben a Mockok Csipler Zoltn
Unit tesztelés Swiftben Célkeresztben a Mock-ok Csipler Zoltán 2019. 04. 29
Unit tesztelés Swift-ben mito. clever things // Arrange id mock. Interactor = OCMClass. Mock([Detail. Interactor class]); id mock. View = OCMClass. Mock([Detail. View. Controller class]); Bevezetés Detail. Presenter *presenter = [Detail. Presenter new]; presenter. interactor = mock. Interactor; presenter. view = mock. View; Nagy kódbázis, modularizált komplex projekt, VIPER Detail. Info � *detail. Info = [[Detail. Info alloc] init. With. Title: @"Detail. Title"]; Cél: minél nagyobb test coverage OCMStub([mock. Interactor provide. Detail. Info]). and. Return(detail. Info); � OCMExpect([mock. View set. Detail. Title: @"Detail. Title"]); � Minél kisebb egységek tesztelése -> Stub, verify -> Mockolás // Act � setup. Detail. Screen]; [presenter Obj-C - OCMock lib (runtime access) Swift - nincs runtime access Detail. Info � *detail. Info = [self. interactor provide. Detail. Info]; // Verify [self. view set. Detail. Title: detail. Info. detail. Title]; [mock. Interactor verify]; [mock. View verify]; Csipler Zoltán 1/8 2019. 04. 29
Unit tesztelés Swift-ben mito. clever things Lehetséges megoldások � Saját mock class-ok írása - költséges időben � 3 rd party lib használata - limitációk, maintenance A kettőt kombinálni Csipler Zoltán 2/8 2019. 04. 29
Unit tesztelés Swift-ben mito. clever things A mi megoldásunk � 3 rd party mock generator lib használata: Cuckoo � Protokollokat mockolunk � Tesztek írása: service layer, interactor, presenter Csipler Zoltán 3/8 2019. 04. 29
Unit tesztelés Swift-ben mito. clever things struct __Stubbing. Proxy_Detail. Interactor. Protocol: Cuckoo. Stubbing. Proxy { { struct __Verification. Proxy_Detail. Interactor. Protocol: describe("fetch. CMSData") { class Mock. Detail. Interactor. Protocol: Detail. Interactor. Protocol, Cuckoo. Protocol. Mock class Quick. Spec { Cuckoo. Verification. Proxy //Credentials. Login. Interactor. Tests: Arrange let project. Dir = Process. Info. process. Info. environment["PROJECT_DIR", default: ". "] { let detail. Info = interactor. provide. Detail. Info() private Cuckoo. Mock. Manager before. Each { ={ let typealias Mocks. Type = cuckoo_manager: Detail. Interactor. Protocol override func spec() let mock. Interactor Mock. Detail. Interactor. Protocol() view? . set. Detail. Title(detail. Info. detail. Title) private call. Matcher: Cuckoo. Call. Matcher stub(mock. Presenter) { mock. Presenter in typealias =let __Stubbing. Proxy_Detail. Interactor. Protocol describe("Credentials. Login. Interactor") { let =Stubbing "(project. Dir)/Tests/Generated. Mocks. swift" letoutput mock. View = Mock. Detail. View. Controller. Protocol() init(manager: Cuckoo. Mock. Manager) { private let source. Location: Cuckoo. Source. Location when(mock. Presenter. present. Credentials. Login(with: any())). then. Do. Nothing() typealias Verification =Mock. Credentials. Login. Interactor. Output. Protocol! __Verification. Proxy_Detail. Interactor. Protocol mock. Presenter: letvar detail. Presenter = Detail. Presenter() self. cuckoo_manager } __default. Impl. Stub: private var Detail. Interactor. Protocol? var seperate mock. Credentials. Login. Service: Mock. Credentials. Login. Service. Protocol! //detail. Presenter. interactor Use variables for each file. = = mock. Interactor }init(manager: call. Matcher: Cuckoo. Call. Matcher, source. Location: Cuckoo. Source. Location) { } mock. Auth. Data. Provider: var Mock. Auth. Data. Provider. Protocol! let generator. Arguments = [ Cuckoo. Mock. Manager, detail. Presenter. view = mock. View self. cuckoo_manager = manager context("show. Forgotten. Username is false") { let"generate", cuckoo_manager = Cuckoo. Mock. Manager. preconfigured. Manager ? ? Cuckoo. Mock. Manager(has. Parent: false) = call. Matcher before. Each { var sut: Credentials. Login. Interactor! let"--testable", detail. Info = self. call. Matcher Detail. Info(detail. Title: "Detail. Title") func provide. Detail. Info() -> Detail. Info> { self. source. Location = source. Location stub(mock. Auth. Data. Provider) { mock. Auth. Data. Provider in func provide. Detail. Info() -> Detail. Info { Cuckoo. Protocol. Stub. Function<(), "Cuckoo", [Cuckoo. Parameter. Matcher<Void>] = [] }when(mock. Auth. Data. Provider. show. Forgotten. Password. get). then. Return(false) before. Each {let matchers: "--exclude", stub(mock. Interactor) { mock. Interactor in � } cuckoo_manager. call("provide. Detail. Info() return=. init(stub: cuckoo_manager. create. Stub(for: Mock. Detail. Interactor. Protocol. self, method: return -> Detail. Info", mock. Presenter Mock. Credentials. Login. Interactor. Output. Protocol() "Excluded. Test. Class, Excluded. Protocol", when(mock. Interactor). provide. Detail. Info(). then. Return(detail. Info) "provide. Detail. Info() -> Detail. Info", parameter. Matchers: matchers)) parameters: (), mock. Credentials. Login. Service = Mock. Credentials. Login. Service. Protocol() } "--output", � } @discardable. Result sut. fetch. CMSData() escaping. Parameters: mock. Auth. Data. Provider =in. Mock. Auth. Data. Provider. Protocol() output, stub(mock. View) { mock. View (), func provide. Detail. Info() -> Cuckoo. __Do. Not. Use<Detail. Info> { } superclass. Call: "(project. Dir)/Tests/Source/Detail. View. Controller. Protocol. swift", when(mock. View). set. Detail. Title(detail. Info. detail. Title). then. Do. Nothing() � let matchers: [Cuckoo. Parameter. Matcher<Void>] = [] should be called with false parameter") { sutit("present. Credentials. Login = }Credentials. Login. Interactor(credentials. Login. Service: mock. Credentials. Login. Service, } "(project. Dir)/Tests/Source/Detail. Interactor. Protocol. swift", return cuckoo_manager. verify("provide. Detail. Info() -> Detail. Info", call. Matcher: call. Matcher, parameter. Matchers: verify(mock. Presenter). present. Credentials. Login(with: auth. Data. Provider: false) mock. Auth. Data. Provider) "(project. Dir)/Tests/Source/Excluded. Test. Class. swift", �Cuckoo. Mock. Manager. crash. On. Protocol. Superclass. Call() matchers, source. Location: source. Location) } , = mock. Presenter ] // Actsut. presenter } } } default. Call: __default. Impl. Stub!. provide. Detail. Info()) detail. Presenter. setup. Detail. Screen() } } shell(["swift", "run", "cuckoo_generator"] + generator. Arguments, in. Dir: "(project. Dir)/Generator/") } // Verify } } … verify(mock. View). set. Detail. Title(detail. Info. detail. Title) Cuckoo Megadott swift fájlokban lévő osztályokra, protokollok-ra mock-ot generál OCMock, Mockito-hoz hasonló szintaktika Stub, verify, parametermatcher block Quick-el és Nimble-el együtt használtuk Csipler Zoltán 4/8 2019. 04. 29
Unit tesztelés Swift-ben mito. clever things Cuckoo limitációk public class Mock. Operation. Queue: Operation. Queue { � Csak felülcsapható osztályokkal működik public var cancel. All. Operations. Call. Count =0 � Final, private nem használható - protocol mockolásnál nem számít } override public func cancel. All. Operations() { � Statikus metódusok - ritkán használtuk, cancel. All. Operations. Call. Count += 1 nem mockoljuk } � Generics - fixálható manuálisan -> Maintenance-t nehezíti � Apple framework-ök nem mockolhatóak -> Saját mock Csipler Zoltán 5/8 2019. 04. 29
Unit tesztelés Swift-ben mito. clever things Előnyök/Hátrányok � Nagy test coverage �� � Mock-ok automatikus generálásával sok időt megspórolunk �� � Maintenance - Script fájl karbantartása �� � Build hibák sokszor nem egyértelműek �� � Meddig lesz support a 3 rd party lib-hez � Csipler Zoltán 6/8 2019. 04. 29
Unit tesztelés Swift-ben mito. clever things Összefoglalás � Maintenance és komplexitás elsőre soknak tűnhet de idővel ahogy beletanultunk egyre könnyebben használhatóvá vált � Sok feature Cuckoo-ban amiről nem esett szó � A jövőben is használni fogjuk Csipler Zoltán 7/8 2019. 04. 29
Unit tesztelés Swift-ben mito. clever things Referencia � https: //github. com/Brightify/Cuckoo � http: //ocmock. org Csipler Zoltán 8/8 2019. 04. 29
Köszönöm a figyelmet! Csipler Zoltán 2019. 04. 29
- Slides: 10