Skip to content

Latest commit

ย 

History

History
157 lines (111 loc) ยท 4.17 KB

Engineering-for-Testability.md

File metadata and controls

157 lines (111 loc) ยท 4.17 KB

Engineering for Testability

๐Ÿ“… 2019.11.19 (ํ™”)

WWDC 2017 | Session : 414 | Category : Testing

๐Ÿ”—

Engineering for Testability - WWDC 2017 - Videos - Apple Developer

Structure of a Unit Test

  • Prepare input
  • Run the code being tested
  • Verify output

Characteristics of Testable Code

  • Control over inputs
  • Visibility into outputs
  • No hidden stage

Testability Techiques

  • Protocols and parameterization
  • Seperarting logic and effects

Protocols and Parameterization

    @IBAction func openTapped(_ sender: Any) {
    	let mode: String
    	switch segmentedControl.selectedSegmentIndex {
    	case 0: mode = "view"
    	case 1: mode = "edit"
    	default: fatalError("Impossible case")
    	}
    	let url = URL(string: "myappscheme://open?id=\(document.identifier)&mode=\(mode)")!
    
     
    	if UIApplication.shared.canOpenURL(url) {
    		UIApplication.shared.open(url, options: [:], completionHandler: nil)
    	} else {
    		 handleURLError()
    	} 
    }

UI Test

์•ฑ์„ ๋Ÿฐ์นญํ•ด์„œ screen์œผ๋กœ ์ด๋™ํ•ด์„œ, ํƒญ ๋ฉ”๋‰ด๋ฅผ ๋ˆ„๋ฅด๊ณ , Open ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ์„œ ๋‹ค๋ฅธ ์•ฑ์œผ๋กœ ๋ณ€๊ฒฝ ๋˜์—ˆ๋Š”์ง€ ํ™•์ธ

โ†’ run ํ•˜๋Š”๋ฐ ์˜ค๋ž˜ ๊ฑธ๋ฆฐ๋‹ค. ํŠนํžˆ ๋ช‡๊ฐœ์˜ ๋‹ค๋ฅธ doument ๊นŒ์ง€ ํ™•์ธ ํ•˜๋ ค๋ฉด. ๋” ํฐ ๋ฌธ์ œ๋Š”, UI Test๋Š” ์•ฑ์„ ๋ฐ”๊พธ๊ธฐ ์œ„ํ•œ URL์„ ๊ฒ€์‚ฌ ํ•  ์ˆ˜ ์—†๋‹ค

์—ฌ๊ธฐ์„œ๋Š” URL์„ ํ…Œ์ŠคํŠธ ํ•˜๊ณ  ์‹ถ์€ ๊ฑฐ๊ธฐ ๋•Œ๋ฌธ์— Unit Test๊ฐ€ ์ ์ ˆํ•˜๋‹ค.

Unit Test

    func testOpensDocumentURLWhenButtonIsTapped() {
    	let controller = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "Preview") as! PreviewViewControllercontroller
    
    	controller.loadViewIfNeeded()
    	controller.segmentedControl.selectedSegmentIndex = 1
    	controller.document = Document(identifier: โ€œTheID")
    	
    	controller.openTapped(controller.button)
    
    	
    }

์œ„์˜ ์ฝ”๋“œ ์ค‘ if UIApplication.shared.canOpenURL(url) ์˜ result ๊ฐ’์€, ๋ฉ”์†Œ๋“œ์˜ ๋‹ค๋ฅธ ์ธํ’‹์— ์˜ํ–ฅ์„ ์ค€๋‹ค

ํ•˜์ง€๋งŒ ์ด๊ฒƒ์€ global system state์— ์˜์กด ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, result์˜ query๋ฅผ ํ”„๋กœ๊ทธ๋žจ์ ์œผ๋กœ ํ…Œ์ŠคํŠธ ํ•  ๋ฐฉ๋ฒ•์ด ์—†๋‹ค

    class DocumentOpener {
    	enum OpenMode: String {
    		case view
    		case edit
    	}
    	
    	func open(_ document: Document, mode: OpenMode) {
    		let modeString = mode.rawValuelet url = URL(string: "myappscheme://open?id=\(document.identifier)&mode=\(modeString)")!
    		
    		if UIApplication.shared.canOpenURL(url) {
    				UIApplication.shared.open(url, options: [:], completionHandler: nil)
    		} else {
    			handleURLError()
    		}
    	}
    }
  • ViewController๋กœ ๋ถ€ํ„ฐ ๋นผ๊ณ  ์‹œ์ž‘ํ•ด๋ณด์ž

  • ๋กœ์ง๊ณผ ํ–‰๋™์„ encapsulate ํ•˜๊ธฐ ์œ„ํ•œ Document opener class์ด๋‹ค

  • ํ•˜์ง€๋งŒ ์—ฌ์ „ํžˆ UIApplication.shared.canOpenURL(url) ์„ ๊ณ ์น  ํ•„์š”๊ฐ€ ์žˆ๋‹ค.

    class DocumentOpener { let application: UIApplicationinit(application: UIApplication) { self.application = application }

    	func open(_ document: Document, mode: OpenMode) {
    		let modeString = mode.rawValue
    		let url = URL(string: "myappscheme://open?id=\(document.dientifier)&mode=\(modeString")!
    
    		if application.canOpenURL(url) {
    			application.open(url, options: [:], completionGandler: nil)
    		} else {
    			handleURLError()
    		}
    	}
    }
    class MockURLOpener: URLOpening {
    	 var canOpen = false
    	 var openedURL: URL?
     
    	func canOpenURL(_ url: URL) -> Bool {
    		return canOpen
    	}
    
    	func open(_ url: URL,options: [String: Any],completionHandler: ((Bool) -> Void)?) {
    		openedURL = url
    	} 
    }

Separating Logic and Effects

Balance between UI and Unit Tests

Unit tests great for testing small, hard-to-reach code paths

UI tests are better at testing integration of larger pieces

Writing code to help UI tests scale

Abstracting UI element queries

  • Store parts of queries in a variable
  • Wrap complex queries in utility methods
  • Reduces noise and clutter in UI test

Creating objects and utility functions

Utilizing keyboard shortcuts