Testes: A que seráque se destina?Breve história sobre testes, boas e más práticas no ecossistema AppleRonan Rodrigo Nunes
View Slide
Arqueologia
2005Xcode 2.1OCUnit2013Xcode 5.0XCTest2015Xcode 7.0XCUITest
Cultura
Não vejo benefício“
Meu código funcionaperfeitamente, por queeu testaria?“
Até APP com testes tembugs“
Não sei como,nem o que testar“
Não sei como,nem o que testar“classonaclassinhaextrairinjetartestar✔
Não temos tempo“
É difícil e complicado“
Meu time não tem acultura de testar“mobster.cc
O que ganho com isso?
👃 Cheiro de que?
🏰 Arquitete-se
🧘 Equilibrio
⛹ Domínio
✍ Documentação
Boas práticas &Smells &Armadilhas
func test_primaryButtonTapped_doesRouteToFullSheet() {// givenXCTAssertTrue(router.routeToFullSheetCallCount == 0)// wheninteractor.primaryButtonTapped()// thenXCTAssertTrue(router.routeToFullSheetCallCount == 1)}✘
func test_primaryButtonTapped_doesRouteToFullSheet() {XCTAssertTrue(router.routeToFullSheetCallCount == 0)interactor.primaryButtonTapped()XCTAssertTrue(router.routeToFullSheetCallCount == 1)}✘
func test_primaryButtonTapped_doesRouteToFullSheet() {interactor.primaryButtonTapped()XCTAssertTrue(router.routeToFullSheetCallCount == 1)}✘
func test_primaryButtonTapped_doesRouteToFullSheet() {interactor.primaryButtonTapped()XCTAssertEqual(router.routeToFullSheetCallCount, 1)}✔︎
func test_startReport_doesRouteToReportFlow() {interactor.startReport()XCTAssertEqual(router.routeToReportFlowCallCount, 1)}✔︎
func test_allByMonth_existingMonth_callCompletionWithItems() {var expectedGroupedFoods: [GroupedFoods]?// ...guard let expectedGroupedFoods = expectedGroupedFoods,firstGroup = expectedGroupedFoods.first else {XCTFail("empty expectedGroupedFoods")}XCTAssertFalse(firstGroup.foods.isEmpty)}✘
func test_allByMonth_existingMonth_callCompletionWithItems() {var expectedGroupedFoods: [GroupedFoods]?// ...XCTAssertFalse(expectedGroupedFoods!.first!.foods.isEmpty)}✘
func test_allByMonth_existingMonth_callCompletionWithItems() {var expectedGroupedFoods: [GroupedFoods]?// ...if let expectedGroupedFoods = expectedGroupedFoods {if let firstGroup = expectedGroupedFoods.first {XCTAssertFalse(firstGroup.foods.isEmpty)}}}✘
func test_allByMonth_existingMonth_callCompletionWithItems() {var expectedGroupedFoods: [GroupedFoods]?// ...XCTAssertEqual(expectedGroupedFoods?.first?.foods.isEmpty,false)XCTAssertEqual(expectedGroupedFoods?.first?.foods.count, 4)}✔︎
func test_allByMonth_existingMonth_callCompletionWithItems() {var expectedGroupedFoods: [GroupedFoods]?let month = MonthGatewayFactory.make().month(number: 1)gateway.all(byMonth: month) { expectedGroupedFoods = $0.data }XCTAssertEqual(expectedGroupedFoods?.isEmpty, false)}✘
func test_allByMonth_existingMonth_callCompletionWithItems() {var expectedGroupedFoods: [GroupedFoods]?let month = MonthGatewayFactory.make().month(number: 1)gateway.all(byMonth: month) {expectedGroupedFoods = $0.data}XCTAssertEqual(expectedGroupedFoods?.isEmpty, false)}✔︎
func test_finishReport_completeFlow() {var completed = falseinteractor.myEventStream().subscribe { _ incompleted = true}.disposed(by: disposeBag)interactor.finish()XCTAssertTrue(completed)}✘
func test_finishReport_completeFlow() {var completed = falseonCompleteFlow(completed = true)interactor.finish()XCTAssertTrue(completed)}func onCompleteFlow(_ fn: @escaping @autoclosure () -> ()) {interactor.myEventStream().subscribe { _ in fn() }.disposeOnTearDown(testCase: self)}✔︎
class ReportTripInteractorTests: XCTestCaseclass ReportTripInteractor_StartTripTests:ReportTripInteractorTestsclass ReportTripInteractor_FinishingTripTests:ReportTripInteractorTestsclass ReportTripInteractor_SearchingTripTests:ReportTripInteractorTestsclass ReportTripInteractor_WaintingTripTests:ReportTripInteractorTests✔︎
func test_start_validInteractor_createEventAtStream() {let expectedResponse = Response(uuid: "id")shareService.fetchHandler = { _ in .just(expectedResponse) }worker.start(interactor)someStream.dataSubject.subscribe(onNext: { eventResponse inXCTAssertEqual(expectedResponse, eventResponse)}).disposed(by: disposeBag)}✘
class Worker {func start(_ interactor: AnyInteractor) {someObservable.subscribe(someStream.dataSubject).disposeOnStop(self)}}.subscribe(someStream.dataSubject)
class Worker {func start(_ interactor: AnyInteractor) {someObservable.disposeOnStop(self)}}
func test_start_validInteractor_createEventAtStream() {let expectedResponse = Response(uuid: "id")shareService.fetchHandler = { _ in .just(expectedResponse) }var eventResponse: Response?someStream.dataSubject.subscribe(onNext: { eventResponse = $0 }).disposed(by: disposeBag)worker.start(interactor)XCTAssertEqual(expectedResponse, eventResponse)}✔︎
func test_schedule_createEvent() {// ...}✘
func test_givenMonthExist_whenContainsMeeting_thenCreateEvent() {// ...}func test_monthExist_containsMeeting_createEvent() {// ...}func testScheduleEvent_monthExist_containsMeeting_createEvent() {// ...}func test_itMonthExist_andContainsMeeting_shouldCreateEvent() {// ...}✔︎
Obrigado
Obrigadoronanrodrigo.dev
Obrigadoronanrodrigo.devronanrodrigo.dev/slides