SlideShare a Scribd company logo
1 of 65
Download to read offline
Unit Test 最後⼀一哩路路
Hokila Jan
iOSDC
2
Hokita Jan
since iOS 4
5 years in ObjC, 2 years in Swift
video streaming / DRM / IAP
Unit Test / Refactor / AB Test
3
什什麼是 Unit Test
ModuleInput Output
3
什什麼是 Unit Test
ModuleInput Output
固定 input ,經過 Module 處理理,檢查 Output
4
為什什麼沒有寫 Unit Test - 常⾒見見說法
feature 都來來不及做了了,哪有時間寫
我們家有 QA
有 UI Test 啊
5
為什什麼沒有寫 Unit Test - 實際上
不會寫
不會控制 Input (API , 不是⾃自⼰己寫的 SDK)
ViewController 超級無敵⼤大,看到就肚爛
實際案例例
login View Controller
7
LoginViewController
login API
AccountKit login API
Facebook login API
save Token after login success
tracking event
set User Info
8
問題 - dependency 太多
login API,成功 or 失敗
facebook / AccountKit SDK ⾏行行為
是否有存下 AccessToken
User 資料是不是該有的都有 (UserID , MemberShip)
該有的 tracking 是不是有送
login 時
login 後
9
UI ViewController
Login API
Facebook
AccessToken
ViewController 功能太多
無法測試
User
Amplitude
Step 1. 拆出 login manager
Step 1. 拆出 login manager
不知道在幹嘛的 module 叫 manager 就對了了
11
class LoginManager{
weak var delegate:LoginManagerDelegate?
func loginFB()
func loginAccountKit()
}
11
protocol LoginManagerDelegate:class{
func loginStart()
func loginSuccess(status:LoginStatus)
func loginFail(json:[String:Any]?,error:NSError?)
}
class LoginManager{
weak var delegate:LoginManagerDelegate?
func loginFB()
func loginAccountKit()
}
12
class LoginController: BaseViewController{
func touchFBlogin(_ sender: AnyObject) {
self.loginManager.loginFB()
}
func touchAClogin(_ sender: AnyObject){
self.loginManager.loginAccountKit()
}
}
extension LoginViewController:LoginManagerDelegate{
func loginStart(){ //show indicator }
func loginFail(json:[String:Any]?,error:NSError?){ //error handling}
func loginSuccess(status:LoginStatus){ //success flow }
}
13
LoginViewController LoginManager
login API
AccountKit login API
Facebook login API
save Token after login success
tracking event
set User Info
login start
login fail handler
login success handler
13
LoginViewController LoginManager
login API
AccountKit login API
Facebook login API
save Token after login success
tracking event
set User Info
login start
login fail handler
login success handler
13
LoginViewController LoginManager
login API
AccountKit login API
Facebook login API
save Token after login success
tracking event
set User Info
login start
login fail handler
login success handler
14
Login
Manager
login API
AccountKit login API
Facebook login API
LoginManagerDelegate
save Token
tracking event
set User Info
Step 2. dependency injection
16
UI ViewController
Login API
Facebook
AccessToken
User
Amplitude
16
UI ViewController
Login API
Facebook
AccessToken
User
Amplitude
Login API
Delegate
17
模擬 login API ⾏行行為
protocol LoginAPIDelegate {
func loginToken(token:String?,callback:@escaping loginAPICompletion)
}
17
17
模擬 login API ⾏行行為
protocol LoginAPIDelegate {
func loginToken(token:String?,callback:@escaping loginAPICompletion)
}
extension API:LoginAPIDelegate{
func loginToken(token:String?,callback:@escaping loginAPICompletion{
// call real API here
}
}
17
17
模擬 login API ⾏行行為
protocol LoginAPIDelegate {
func loginToken(token:String?,callback:@escaping loginAPICompletion)
}
class MockLoginAPI:LoginAPIDelegate {
func loginToken(token:String?,callback:@escaping loginAPICompletion {
//use mock response here
}
}
extension API:LoginAPIDelegate{
func loginToken(token:String?,callback:@escaping loginAPICompletion{
// call real API here
}
}
17
18
模擬 Facebook SDK response ⾏行行為
protocol FBLoginManagerDelegate {
var currentAccessToken:String? { get }
}
class FBLoginManager:FBLoginManagerDelegate {
var currentAccessToken:String? {
return FBSDKAccessToken.current()?.tokenString
}
}
class MockFBLogin:FBLoginManagerDelegate {
var currentAccessToken: String?
}
19
class LoginManager{
weak var delegate:LoginManagerDelegate?
private var loginAPI:LoginAPIDelegate
private let accountKit:AccountKitDelegate
private let fb:FBLoginManagerDelegate
init(api:LoginAPIDelegate = API.shareInstance,
ac:AccountKitDelegate = AKFAccountKit(responseType: .accessToken),
fblogin:FBLoginManagerDelegate = FBLoginManager()) {
self.loginAPI = api
self.accountKit = ac
self.fb = fblogin
}
}
Real Instance
20
class MockLoginAPI:LoginAPIDelegate {
func loginToken(token:String?,callback:@escaping loginAPICompletion {
//use mock response here
}
}
Mock Login API
20
class MockLoginAPI:LoginAPIDelegate {
func loginToken(token:String?,callback:@escaping loginAPICompletion {
//use mock response here
}
}
Mock Login API
class MockLoginAPI:LoginAPIDelegate {
var loginCallback:(LoginStatus,[String:Any]?, AccessToken?, NSError?)
func loginToken(token:String?,callback:@escaping loginAPICompletion {
callback(loginCallback.0,loginCallback.1,
loginCallback.2,loginCallback.3)
}
}
21
class MockFBLogin:FBLoginManagerDelegate {
var currentAccessToken: String?
}
Mock FB SDK
Step 3. write test
23
class KTLoginManagerTests:KKTVTests {
private var mockLoginAPI = MockLoginAPI()
private var mockAC = MockAccountKit()
private var mockFBLogin = MockFBLogin()
private var mockAccessTokenManager = MockAccessTokenManager()
private var mockTracker = MockTracker()
private var mockLoginVC = MockLoginVC()
private var mockUser = MockUser()
}
23
class KTLoginManagerTests:KKTVTests {
private var mockLoginAPI = MockLoginAPI()
private var mockAC = MockAccountKit()
private var mockFBLogin = MockFBLogin()
private var mockAccessTokenManager = MockAccessTokenManager()
private var mockTracker = MockTracker()
private var mockLoginVC = MockLoginVC()
private var mockUser = MockUser()
}
override func setUp() {
super.setUp()
// rest all mock object
}
24
func testFBloginSuccess() {
//1.prepare input
mockFBLogin.currentAccessToken = "123456789"
let loginJSON = loadData(fileName: "loginSuccess", type: "json")
mockLoginAPI.loginCallback = (.loginSuccess,loginJSON,authToken,nil)
24
func testFBloginSuccess() {
//1.prepare input
mockFBLogin.currentAccessToken = "123456789"
let loginJSON = loadData(fileName: "loginSuccess", type: "json")
mockLoginAPI.loginCallback = (.loginSuccess,loginJSON,authToken,nil)
//2.Compose Login Manager
loginManager = KTLoginManager(api: mockLoginAPI,
fblogin: mockFBLogin)
loginManager.delegate = mockLoginVC
24
func testFBloginSuccess() {
//1.prepare input
mockFBLogin.currentAccessToken = "123456789"
let loginJSON = loadData(fileName: "loginSuccess", type: "json")
mockLoginAPI.loginCallback = (.loginSuccess,loginJSON,authToken,nil)
//2.Compose Login Manager
loginManager = KTLoginManager(api: mockLoginAPI,
fblogin: mockFBLogin)
loginManager.delegate = mockLoginVC
//3.trigger login
loginManager.loginFB()
//4.Verify output
……..
}
25
func testFBloginSuccess() {
……..
//4.Verify output
XCTAssertTrue(mockLoginVC.isLoginSuccess)
XCTAssertNotNil(mockAccessTokenManager.lastAccessToken)
XCTAssertEqual(mockUser.info.memberShip, .premium)
XCTAssertNotNil(mockUser.info.userID)
XCTAssertEqual(mockTracker.lastStatus, .loginSuccess)
XCTAssertEqual(mockTracker.lastLoginType, .facebook)
XCTAssertNotNil(mockTracker.lastLoginUserInfo.userID)
}
26
什什麼是 Unit Test
ModuleInput Output
26
什什麼是 Unit Test
ModuleInput Output
固定 input ,經過 Module 處理理,檢查 Output
27
UI
Presenter
Login API
provider
Facebook
AccessToken
Manager
LoginManager
Login
API
User
Tracking
manager
AccessToken
Delegate
Facebook
Delegate
User info
User setting
Amplitude
Action
原本的 View Controller 剩餘功能
Access
Token
27
UI
Presenter
Login API
provider
Facebook
AccessToken
Manager
Login API
Delegate
LoginManager
Login
API
User
Tracking
manager
AccessToken
Delegate
Facebook
Delegate
User info
User setting
Amplitude
Action
原本的 View Controller 剩餘功能
Access
Token
27
UI
Presenter
Login API
provider
Facebook
AccessToken
Manager
Login API
Delegate
LoginManager
User
Tracking
manager
AccessToken
Delegate
Facebook
Delegate
User info
User setting
Amplitude
Action
原本的 View Controller 剩餘功能
Access
Token
dependency injection ⼩小技巧
inject 的 module 太多,不確定有沒有漏
30
class LoginManager{
static let shareInstance = LoginManager()
weak var delegate:LoginManagerDelegate?
private var loginAPI:LoginAPIDelegate
private let accountKit:AccountKitDelegate
private let fb:FBLoginManagerDelegate
init(api:LoginAPIDelegate = API.shareInstance,
ac:AccountKitDelegate = AKFAccountKit(responseType: .accessToken),
fblogin:FBLoginManagerDelegate = FBLogInManager()) {
self.loginAPI = api
self.accountKit = ac
self.fb = fblogin
}
}
Real Instance
31
struct LoginManagerInject{
var loginAPI:KTLoginAPIDelegate = KTAPI.shareInstance
var accountKit:KTAccountKitDelegate = AKFAccountKit(responseType: .accessTok
var fb:KTFBLoginManagerDelegate = FBLogInManager()
}
31
struct LoginManagerInject{
var loginAPI:KTLoginAPIDelegate = KTAPI.shareInstance
var accountKit:KTAccountKitDelegate = AKFAccountKit(responseType: .accessTok
var fb:KTFBLoginManagerDelegate = FBLogInManager()
}
class LoginManager{
static let shareInstance = LoginManager()
weak var delegate:LoginManagerDelegate?
private let inject:LoginManagerInject
init(inject:LoginManagerInject = LoginManagerInject() ){
self.inject = inject
}
}
31
struct LoginManagerInject{
var loginAPI:KTLoginAPIDelegate = KTAPI.shareInstance
var accountKit:KTAccountKitDelegate = AKFAccountKit(responseType: .accessTok
var fb:KTFBLoginManagerDelegate = FBLogInManager()
}
self.loginAPI
class LoginManager{
static let shareInstance = LoginManager()
weak var delegate:LoginManagerDelegate?
private let inject:LoginManagerInject
init(inject:LoginManagerInject = LoginManagerInject() ){
self.inject = inject
}
}
31
struct LoginManagerInject{
var loginAPI:KTLoginAPIDelegate = KTAPI.shareInstance
var accountKit:KTAccountKitDelegate = AKFAccountKit(responseType: .accessTok
var fb:KTFBLoginManagerDelegate = FBLogInManager()
}
self.inject.loginAPIself.loginAPI
class LoginManager{
static let shareInstance = LoginManager()
weak var delegate:LoginManagerDelegate?
private let inject:LoginManagerInject
init(inject:LoginManagerInject = LoginManagerInject() ){
self.inject = inject
}
}
inject init function
33
User
UserInfo
UserSetting
User Info save in memory
Ex:User ID, Member Ship
User Setting save in NSUserDefault
Ex:IsShowXXPage
When UserInfo change , some UserSetting Variable also change
Requirement
AppDelegate
34
User
UserInfo
UserSetting
fileprivate(set) var memberShip:MemberShip = .unassign {
willSet(newValue){
//change from premium to expire
if self.memberShip == .premium && newValue == .expired {
shareAppDelegate.user.setting.hasShowExpireNotice = false
}
}
}
35
class UserInfo {
private var userSetting:UserSettingDelegate
init(userSetting:UserSettingDelegate = shareAppDelegate.user.setting){
self.userSetting = userSetting
}
}
User UserInfoAppDelegat
User UserSettingAppDelegate
36
class UserInfo {
private var userSetting:UserSettingDelegate
init(userSetting:UserSettingDelegate = shareAppDelegate.user.setting){
self.userSetting = userSetting
}
}
User UserInfoAppDelegat
User
UserSetting
AppDelegate
UserInfo
37
class UserInfo {
private var userSetting:UserSettingDelegate
init(userSetting:UserSettingDelegate = shareAppDelegate.user.setting){
self.userSetting = userSetting
}
}
User UserInfoAppDelegat
User
UserSetting
AppDelegate
UserInfo
User UserSettingAppDelegate
38
class UserInfo:{
private var injectFunc:()->UserSetting
private var setting:UserSetting {
return self.injectFunc()
}
init(injectFunc:@escaping ()->UserSetting =
{return shareDelegate.user.setting}){
self.injectFunc = injectFunc
}
}
38
class UserInfo:{
private var injectFunc:()->UserSetting
private var setting:UserSetting {
return self.injectFunc()
}
init(injectFunc:@escaping ()->UserSetting =
{return shareDelegate.user.setting}){
self.injectFunc = injectFunc
}
}
stored property
38
class UserInfo:{
private var injectFunc:()->UserSetting
private var setting:UserSetting {
return self.injectFunc()
}
init(injectFunc:@escaping ()->UserSetting =
{return shareDelegate.user.setting}){
self.injectFunc = injectFunc
}
}
stored property
computed property
38
class UserInfo:{
private var injectFunc:()->UserSetting
private var setting:UserSetting {
return self.injectFunc()
}
init(injectFunc:@escaping ()->UserSetting =
{return shareDelegate.user.setting}){
self.injectFunc = injectFunc
}
}
self.setting
stored property
computed property
39
殘酷的事實
新創公司的開發模式
花了了⼀一倍的時間寫 Feature,但是邏輯都混在⼀一起沒有拆 module
要 refactor + 補 Unit Test 的時間,跟原本做 Feature 的時間⼀一樣
猶豫
40
循環
寫了了⼀一個 Feature,但是邏輯都混在⼀一起沒有拆 module
要補 Unit Test 好累喔,要 refactor 好危險
不敢動,不知道怎麼拆 module
有了了新 Request
寫了了⼀一個新Feature,但是邏輯都混在⼀一起沒有拆 module
…………….
41
寫 Unit Test 帶來來的好處
• 同樣的 code ⽤用兩兩種思考⽅方式看過
• 重新檢視每個 class 的功能
• ⾃自然⽽而然學會切 module,找出可以共⽤用的商業邏輯
• ⾃自動跑,⼀一次跑完幾百個幾千的 check,令⼈人安⼼心
42
作對事 vs 不要做錯事
• 花了了⼀一個⽉月做了了新 feature ,推出後市場反應平淡
• 花了了5秒不⼩小⼼心改到共⽤用 class 的邏輯,所有 user 受影響
• 做了了⼀一堆 feature ,user 可能會喜歡可能不會喜歡,

但是重要功能出 bug,那就死定了了
43
要怎麼開始?
• 列列出 priority,重要的事要⾺馬上做。⼀一個⽉月後還沒開始做,那就是不重要的事
• 絕對不可以錯的 module + 會⼤大量量 refuse 的 model
• 下次寫新 module 的時候先想 input output
• 祝各位⼯工程師從此過著幸福快樂的⽇日⼦子
44
相關閱讀
• wwdc 2017 session 414: Engineering for Testability
• Test-Driven iOS Development with Swift 4
• Working Effectively with Legacy Code
• The Art of Unit Testing

More Related Content

Similar to Unit Test 最後一哩路

Page Objects Done Right - selenium conference 2014
Page Objects Done Right - selenium conference 2014Page Objects Done Right - selenium conference 2014
Page Objects Done Right - selenium conference 2014Oren Rubin
 
Functional Reactive Programming - RxSwift
Functional Reactive Programming - RxSwiftFunctional Reactive Programming - RxSwift
Functional Reactive Programming - RxSwiftRodrigo Leite
 
JavaEE Security
JavaEE SecurityJavaEE Security
JavaEE SecurityAlex Kim
 
How to implement sso using o auth in golang application
How to implement sso using o auth in golang applicationHow to implement sso using o auth in golang application
How to implement sso using o auth in golang applicationKaty Slemon
 
Introduction to aop
Introduction to aopIntroduction to aop
Introduction to aopDror Helper
 
jQuery Bay Area Conference 2010
jQuery Bay Area Conference 2010jQuery Bay Area Conference 2010
jQuery Bay Area Conference 2010mennovanslooten
 
EWD 3 Training Course Part 41: Building a React.js application with QEWD, Part 5
EWD 3 Training Course Part 41: Building a React.js application with QEWD, Part 5EWD 3 Training Course Part 41: Building a React.js application with QEWD, Part 5
EWD 3 Training Course Part 41: Building a React.js application with QEWD, Part 5Rob Tweed
 
Python from zero to hero (Twitter Explorer)
Python from zero to hero (Twitter Explorer)Python from zero to hero (Twitter Explorer)
Python from zero to hero (Twitter Explorer)Yuriy Senko
 
Highload JavaScript Framework without Inheritance
Highload JavaScript Framework without InheritanceHighload JavaScript Framework without Inheritance
Highload JavaScript Framework without InheritanceFDConf
 
Découplez votre appli en micro-APIs
Découplez votre appli en micro-APIsDécouplez votre appli en micro-APIs
Découplez votre appli en micro-APIsNicolas Blanco
 
How to implement golang jwt authentication and authorization
How to implement golang jwt authentication and authorizationHow to implement golang jwt authentication and authorization
How to implement golang jwt authentication and authorizationKaty Slemon
 
API Details For Ascitconsultancyservices.com
API Details For Ascitconsultancyservices.comAPI Details For Ascitconsultancyservices.com
API Details For Ascitconsultancyservices.comCarmor Bass
 
Api details for american syscorp
Api details for american syscorpApi details for american syscorp
Api details for american syscorpCarmor Bass
 

Similar to Unit Test 最後一哩路 (20)

Page Objects Done Right - selenium conference 2014
Page Objects Done Right - selenium conference 2014Page Objects Done Right - selenium conference 2014
Page Objects Done Right - selenium conference 2014
 
Functional Reactive Programming - RxSwift
Functional Reactive Programming - RxSwiftFunctional Reactive Programming - RxSwift
Functional Reactive Programming - RxSwift
 
JavaEE Security
JavaEE SecurityJavaEE Security
JavaEE Security
 
Os Johnson
Os JohnsonOs Johnson
Os Johnson
 
iOS_Presentation
iOS_PresentationiOS_Presentation
iOS_Presentation
 
Tuto jtatoo
Tuto jtatooTuto jtatoo
Tuto jtatoo
 
How to implement sso using o auth in golang application
How to implement sso using o auth in golang applicationHow to implement sso using o auth in golang application
How to implement sso using o auth in golang application
 
Introduction to aop
Introduction to aopIntroduction to aop
Introduction to aop
 
jQuery Bay Area Conference 2010
jQuery Bay Area Conference 2010jQuery Bay Area Conference 2010
jQuery Bay Area Conference 2010
 
EWD 3 Training Course Part 41: Building a React.js application with QEWD, Part 5
EWD 3 Training Course Part 41: Building a React.js application with QEWD, Part 5EWD 3 Training Course Part 41: Building a React.js application with QEWD, Part 5
EWD 3 Training Course Part 41: Building a React.js application with QEWD, Part 5
 
Python from zero to hero (Twitter Explorer)
Python from zero to hero (Twitter Explorer)Python from zero to hero (Twitter Explorer)
Python from zero to hero (Twitter Explorer)
 
Highload JavaScript Framework without Inheritance
Highload JavaScript Framework without InheritanceHighload JavaScript Framework without Inheritance
Highload JavaScript Framework without Inheritance
 
Geb qa fest2017
Geb qa fest2017Geb qa fest2017
Geb qa fest2017
 
Spring Security.ppt
Spring Security.pptSpring Security.ppt
Spring Security.ppt
 
Découplez votre appli en micro-APIs
Découplez votre appli en micro-APIsDécouplez votre appli en micro-APIs
Découplez votre appli en micro-APIs
 
How to implement golang jwt authentication and authorization
How to implement golang jwt authentication and authorizationHow to implement golang jwt authentication and authorization
How to implement golang jwt authentication and authorization
 
Facebook api
Facebook apiFacebook api
Facebook api
 
Webauthn Tutorial
Webauthn TutorialWebauthn Tutorial
Webauthn Tutorial
 
API Details For Ascitconsultancyservices.com
API Details For Ascitconsultancyservices.comAPI Details For Ascitconsultancyservices.com
API Details For Ascitconsultancyservices.com
 
Api details for american syscorp
Api details for american syscorpApi details for american syscorp
Api details for american syscorp
 

More from Hokila Jan

Tracking Event validator
Tracking Event validatorTracking Event validator
Tracking Event validatorHokila Jan
 
Preparation for wwdc and not waste it
Preparation for wwdc and not waste itPreparation for wwdc and not waste it
Preparation for wwdc and not waste itHokila Jan
 
Third party module strategy
Third party module strategyThird party module strategy
Third party module strategyHokila Jan
 
SwiftyJSON 慘痛經驗
SwiftyJSON   慘痛經驗SwiftyJSON   慘痛經驗
SwiftyJSON 慘痛經驗Hokila Jan
 
How to cheat jb detector and detect cheating
How to cheat jb detector and detect cheatingHow to cheat jb detector and detect cheating
How to cheat jb detector and detect cheatingHokila Jan
 
進擊的帳單
進擊的帳單進擊的帳單
進擊的帳單Hokila Jan
 
讓你的App優雅的crash三部曲
讓你的App優雅的crash三部曲讓你的App優雅的crash三部曲
讓你的App優雅的crash三部曲Hokila Jan
 
iOS app security
iOS app security  iOS app security
iOS app security Hokila Jan
 
接案公司的日子
接案公司的日子接案公司的日子
接案公司的日子Hokila Jan
 
快思慢想Ch13 14
快思慢想Ch13 14快思慢想Ch13 14
快思慢想Ch13 14Hokila Jan
 
從Scrum到放棄scrum
從Scrum到放棄scrum從Scrum到放棄scrum
從Scrum到放棄scrumHokila Jan
 
快思慢想讀書會Ch9 10
快思慢想讀書會Ch9 10快思慢想讀書會Ch9 10
快思慢想讀書會Ch9 10Hokila Jan
 

More from Hokila Jan (12)

Tracking Event validator
Tracking Event validatorTracking Event validator
Tracking Event validator
 
Preparation for wwdc and not waste it
Preparation for wwdc and not waste itPreparation for wwdc and not waste it
Preparation for wwdc and not waste it
 
Third party module strategy
Third party module strategyThird party module strategy
Third party module strategy
 
SwiftyJSON 慘痛經驗
SwiftyJSON   慘痛經驗SwiftyJSON   慘痛經驗
SwiftyJSON 慘痛經驗
 
How to cheat jb detector and detect cheating
How to cheat jb detector and detect cheatingHow to cheat jb detector and detect cheating
How to cheat jb detector and detect cheating
 
進擊的帳單
進擊的帳單進擊的帳單
進擊的帳單
 
讓你的App優雅的crash三部曲
讓你的App優雅的crash三部曲讓你的App優雅的crash三部曲
讓你的App優雅的crash三部曲
 
iOS app security
iOS app security  iOS app security
iOS app security
 
接案公司的日子
接案公司的日子接案公司的日子
接案公司的日子
 
快思慢想Ch13 14
快思慢想Ch13 14快思慢想Ch13 14
快思慢想Ch13 14
 
從Scrum到放棄scrum
從Scrum到放棄scrum從Scrum到放棄scrum
從Scrum到放棄scrum
 
快思慢想讀書會Ch9 10
快思慢想讀書會Ch9 10快思慢想讀書會Ch9 10
快思慢想讀書會Ch9 10
 

Recently uploaded

EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWERMadyBayot
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDropbox
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)wesley chun
 
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Jeffrey Haguewood
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...Martijn de Jong
 
AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024The Digital Insurer
 
Ransomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdfRansomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdfOverkill Security
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoffsammart93
 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024The Digital Insurer
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...apidays
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...apidays
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdflior mazor
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MIND CTI
 
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...Zilliz
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...DianaGray10
 
Navi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Navi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot ModelNavi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Navi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot ModelDeepika Singh
 
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...apidays
 

Recently uploaded (20)

EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor Presentation
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024
 
Ransomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdfRansomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdf
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdf
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
Navi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Navi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot ModelNavi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Navi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot Model
 
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
 

Unit Test 最後一哩路