Category: Uncategorized

  • Async await trong swift – Part 1

    Async await trong swift – Part 1

    Apple đã giới thiệu cho cộng đồng dev iOS về Swift 5.5, trong đó có sự cập nhật rất là lớn. Đó là về bất đồng bộ, với async await

    Để xem async / await giúp ngôn ngữ như thế nào, sẽ hữu ích khi xem cách chúng tôi đã giải quyết vấn đề tương tự trước đây.

    Một ví dụ về networking trước khi async await được update

    class SeverConnection {
        let defaultSession = URLSession(configuration: URLSessionConfiguration.default)
        var dataTask: URLSessionDataTask?
        var downloadTask: URLSessionDownloadTask?
        var uploadTask: URLSessionUploadTask?
    }
    
    extension SeverConnection {
        func fetchAPIFromURL(_ url: String, completion: @escaping (String?, Error?) -> Void) {
            guard let url = URL(string: url) else {
                completion(nil, SeverConnectionError.badURL)
                return
            }
            dataTask = defaultSession.dataTask(with: url, completionHandler: { (data, response, error) in
                if let error = error {
                    completion(nil, SeverConnectionError.errorWithDataTask)
                    return
                }
                guard let httpResponse = response as? HTTPURLResponse,
                    (200...299).contains(httpResponse.statusCode) else {
                        completion(nil, SeverConnectionError.badResponse)
                        return
                }
                guard let data = data else { return }
                let dataString = String(data: data, encoding: .utf8)
                completion(dataString, nil)
            })
            dataTask?.resume()
        }
    }
    

    khi sử dụng:

    private func fetchListData() {
            let urlString = "https://vapor-mock.herokuapp.com/pic_dic.json"
            severConnection.fetchAPIFromURL(urlString) { [weak self] (data, error) in
                guard let self = self else {
                    return
                }
                if let error = error {
                    print(error)
                }
                if let data = data {
                    self.convertData(data)
                }
            }
        }
        
        private func convertData(_ data: String) {
            let responseData = Data(data.utf8)
            let decoder = JSONDecoder()
            
            var responseImageData: [ImageData]?
            
            do {
                responseImageData = try decoder.decode([ImageData].self, from: responseData)
                infoImages = responseImageData
                DispatchQueue.main.async {
                    self.mainTableView.reloadData()
                }
            } catch {
                print("Error decoding imageData: \(error)")
            }
        }
    

    Còn với async await

    class FetchAPITask {
        static let shared = FetchAPITask()
        
        func fetchAPI<D: Decodable>(url: URL) async throws -> D {
            let task = Task { () -> D in
                try await fetchAndDecode(url: url)
            }
            return try await task.value
        }
        
        func fetchAPIGroup<D: Decodable>(urls: [URL]) async throws -> [D] {
            try await withThrowingTaskGroup(of: D.self) { (group)  in
                for url in urls {
                    group.async { try await self.fetchAndDecode(url: url) }
                }
                var results = [D]()
                for try await result in group {
                    results.append(result)
                }
                return results
            }
        }
        
        func fetchAndDecode<D: Decodable>(url: URL) async throws -> D {
            let data = try await URLSession.shared.fetchData(with: url)
            let decodedData = try JSONDecoder().decode(D.self, from: data)
            return decodedData
        }
    }
    

    khi sử dụng:

    func fetchAllAPI() async {
        do {
            if let url = url {
                let datas: [ImageData] = try await FetchAPITask.shared.fetchAPI(url: url)
                self.datas = datas
            }
        } catch {
            print(error)
        }
    }
    
    Task.init(priority: .default, operation: {
        await self.fetchAllAPI()
        DispatchQueue.main.async {
            self.mainTableView.reloadData()
        }
    })
    

    Kết luận

    • Sử dụng async await với syntax đơn giản hơn dễ đọc hơn giúp việc đọc hiểu và maintain sẽ dễ dàng hơn
    • Các vấn đề với closure (điển hình như việc nhiều callback lồng nhau)
    • Đây là một ví dụ về việc sử dụng async await call api async await còn rất nhiều thứ hay ho mọi người có thể đọc tại đây
  • How To Create A Framework In Swift

    How To Create A Framework In Swift

    Updated for Swift 5

    Vì sao nên sử dụng framework

    • Việc sử dụng các tính chất hướng đối tượng trong lập trình rất phổ biến trong đó có tính chất kế thừa
    • Nhưng với việc swiftUI được ra đời và với SwiftUI thì View nó là struct, nên không thể kế thừa
    • Vậy có cách nào để thay thế được kế thừa trong swift?
    • Đối với riêng SwiftUI nói riêng hay việc sử dụng các framework iOS nói chung: Việc sử dụng tính chất kế thừa sẽ có những issue như swiftUI đang sử dụng View là struct ( struct k có các tính chất hướng đối tượng như class). Vâyviệc Customize 1 class để tái sử dụng như Class MainView: CustomizeView hay các phương thức như override hay class cha có các property gì thì class con cũng sẽ có các property đấy

    Tạo framework như thế nào?

    • Từ Xcode 11, Apple đã công bố tạo một framework được gọi là XCFramework
      • Mở Xcode, File > New > Project và chọn Framework

    • Triển khai mã code cho framwork tại đây
    • Tiếp theo sử dụng xcodebuild archive để tạo kho lưu trữ

      xcodebuild archive -scheme PROJECTNAME_HERE -destination=”iOS” -archivePath /tmp/xcf/ios.xcarchive -derivedDataPath /tmp/iphoneos -sdk iphoneos SKIP_INSTALL=NO BUILD_LIBRARIES_FOR_DISTRIBUTION=YES

      xcodebuild archive -scheme PROJECTNAME_HERE -destination=”iOS Simulator” -archivePath /tmp/xcf/iossimulator.xcarchive -derivedDataPath /tmp/iphoneos -sdk iphonesimulator SKIP_INSTALL=NO BUILD_LIBRARIES_FOR_DISTRIBUTION=YES destination: tùy chọn xác định nền tảng mục tiêu và các thiết bị.archivePath: đường dẫn tới thư mục cần tạo framework

    • Sau khi chạy 2 lệnh ở trên thành công
    • Tiếp theo sử dung câu lệnh này để đóng gói framwork

      xcodebuild -create-xcframework -framework /tmp/xcf/ios.xcarchive/Products/Library/Frameworks/PROJECTNAME_HERE.framework -framework /tmp/xcf/iossimulator.xcarchive/Products/Library/Frameworks/PROJECTNAME_HERE.framework -output FRAMEWORK_NAME.xcframework

      File đã được gen ra thành công:

    • Add framework đã tạo vào trong project cần dùng và để sang Embed & Sign 
    • Import framework vào class cần sử dụng

    (more…)

  • Result type trong xử lý Networking

    Result type trong xử lý Networking

    1. Giới Thiệu

    Chào mọi người, mình là Lâm.

    Hôm này mình xin viết 1 bài về Result type trong Swift và cách nó kết hợp với xử lý Networking để viết ra những đoạn code ngắn gọn, súc tích, dễ đọc, dễ maintain. Trước Swift 5 thì nó là 1 custom type phải tự viết, tuy nhiên sau đấy chắc mn thấy nó hay nên từ Swift 5 thì nó đã trở thành 1 type build-in của Swift luôn (https://developer.apple.com/documentation/swift/result) .

    Có thể thấy ngôn ngữ Swift phát triển liên tục và nhiều trong số đấy là từ những ý tưởng đóng góp của cộng đồng developer

    2. Ý tưởng

    Khi xử lý những tác vụ trong lập trình, chúng ta luôn mong muốn là khi input 1 cái gì đấy vào, thì output của nó ra sẽ chỉ là 1 trong 2 case: success hoặc fail. Như vậy thì luồng xử lý của chúng ta sau đấy sẽ rất clear và gọn gàng.
    Tuy nhiên success thì có nhiều kiểu giá trị trả về, fail thì cũng có nhiều kiểu lỗi bắn ra, vậy thì mình cần phải có 1 type để uniform được các trường hợp, quy về 1. Thì đấy chính là mục đích của Result type.

    Ý tưởng của Result type rất đơn giản, nó là 1 generic Enum, gồm 2 generic là Success và Failure, và có 2 case: 1 cho success và 1 cho failure.
    – Case success sẽ chứa value mà chúng ta expect.
    – Case failure sẽ chứa error bắn ra.

    enum Result < Success, Failure: Error > {
        case success(Success)
        case failure(Failure)
    }

    3. Áp dụng

    Nói về xử lý Networking, ví dụ trong mô hình MVC, thường chúng ta sẽ tạo ra 1 func thế này ở Model:

    func loadData(url: URL, completion: @escaping (Data?, Error?) -> Void) {
        let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
            if let error = error {
                completion(nil, error)
            }
             if let data = data {
                 completion(data, nil)
            }
        }
        task.resume()
    
    }


    Rồi ở bên ViewController chúng ta sẽ hứng và xử lý tiếp như thế này:

    model.loadData(url: url) { [weak self] (data, error) in
        guard let self = self else { return }
        if let error = error {
            // handle error...
        }
         if let data = data {
            // handle response...
        }
    }

    Có 2 cái ko hay ở đây:
    – Cái ko hay thứ 1 là với completion: (Data?, Error?) chúng ta sẽ có tổng cộng 4 case xảy ra:
    + (data, error)
    + (data, nil)
    + (nil, error)
    + (nil, nil)
    Case đầu tiên (data, error) và case cuối cùng (nil, nil) là 2 case mà sẽ không bao giờ mong muốn. Vì output ko thể nào vừa có data lại vừa có error, hay output ko có gì cả, như vậy thì sẽ rất confuse.

    – Cái ko hay thứ 2 là ở đầu bên kia (ViewController) khi nhận response từ Model sẽ phải xử lý 2 parameter với type là optional (data và error), rồi lại if/else 1 hồi để handle đủ case.

    Bây giờ nếu chúng ta sử dụng Result type vào thì nó sẽ như thế này:

    func loadData(url: URL, handler: @escaping (Result<Data, Error>) -> Void) {
    
            let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
                if let error = error {
                    completion(.failure(error))
                }
                if let data = data {
                    completion(.success(data))
                }
            }
        }

    và đầu bên kia xử lý sẽ như thế này:

            model.loadData(url: url) { [weak self] result in
                guard let self = self else { return }
                switch result {
                case .failure(let error):
                    // handle error...
                case .success(let data):
                    // handle success...
                }
            }

    sẽ chỉ phải xử lý 1 parameter là result: Result, và Result chỉ có 2 case là success/fail, đúng với những gì cta mong muốn, code nhìn clear hơn nhiều, thay đống if/else bằng switch/case cũng làm code dễ đọc hơn.

    4. Mở rộng

    Về cơ bản là như vậy, mở rộng hơn thì chúng ta có thể define chi tiết hơn về lỗi.
    Nhìn lại declaration:

    enum Result < Success, Failure: Error > {
        case success(Success)
        case failure(Failure)
    }

    chúng ta thấy Failure đang đơn giản adopt vào protocol Error. Error thì rất common, nó khá là chung chung, gọi là lỗi gì vứt vào đây cũng được. Bây giờ muốn specific hơn về lỗi, cta có thể tạo 1 custom type, vì Result của cta nhận vào generic là 1 Error nên custom type của cta chỉ cần adopt vào protocol Error là được:

    enum VideoError: Error {
        case networkFailure(Error)
        case exceedSize
        case invalidFormat
    }

    khi gọi API:

        func loadData(url: URL, completion: @escaping (Result<Data, VideoError>) -> Void) {
    
            let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
    
                if let error = error {
                    completion(.failure(.networkFailure(error)))
                    return
                }
    
                if exeedSize {
                    completion(.failure(.exceedSize))
                    return
                }
    
                if invalidFormat {
                    completion(.failure(.invalidFormat))
                    return
                }
    
                if let data = data {
                    completion(.success(data))
                }
            }
        }

    và đầu bên kia xử lý:

           model.loadData(url: url) { [weak self] result in
                guard let self = self else { return }
                switch result {
                case .success(let data):
                    // handle success...
                case .failure(.networkFailure(let error)):
                    // handle networking error...
                case .failure(.exceedSize):
                    // handle exceedSize...
                case .failure(.invalidFormat):
                    // handle invalid format...
                }
            }

    có thể thấy luồng đi rất clear.

    Bên trên là Result mình tự viết, đối với built-in của Swift 5 thì type còn có 1 số những methods:
    get: để lấy ra success value
    map/flatMap: để transform success value thành 1 dạng khác
    mapError/flatMapError: để transform failure error thành 1 dạng khác
    ==, != : để so sánh xem 2 result có giống nhau hay ko
    Mn có thể đọc chi tiết trong docs:
    https://developer.apple.com/documentation/swift/result

    Nói chung nó sẽ hỗ trợ để mình xử lý thêm cho data/error trả về (hình dung mình có thể viết 1 cái extension để decode success value thành object luôn chẳng hạn).

    OK, hy vọng mn cảm thấy hữu ích.
    Thanks for reading.

  • Tại sao nên hạn chế sử dụng Singleton, static function(util class, Helper class)?

    Tại sao nên hạn chế sử dụng Singleton, static function(util class, Helper class)?

    Đây thực sự là một câu hỏi không dễ để trả lời… Một cách thông thường, trong suy nghĩ của hầu hết lập trình viên sẽ là: chỗ nào giống nhau, gọi lại nhiều lần thì nên gom thành common class, static cho dễ gọi.

    Ok, mọi thứ đều ổn, nhưng nếu ko để ý & quá tay một chút thì nó đã vi phạm nghiêm trọng đến design ứng dụng – nó phá vỡ ranh giới của các element,module hoặc class

    Tại sao lại như vậy? Bản chất Lập trình OOP là mô phỏng thực tế cuộc sống vào chương trình bằng ngôn ngữ lập trình. Thay vì dùng văn tả cảnh, thì lập trình viên tả lại cuộc sống bằng ngôn ngữ lập trình trên một framework nào đấy. Ở ngoài cuộc sống có gì, thì chương trình cũng có cái đó, chúng ta có Sinh viên, có Tài khoản ngân hàng, có Ô tô…etc.

    Nhưng nếu bạn để ý, trong cuộc sống Util class, Helper class, Common class… ko tồn tại. Ai đó tạo lên cuộc sống hẳn phải là một lập trình viên cực kỳ vĩ đại.

    Phân ranh giới. (Boundaries)

    Software architect là một nghệ thuật để tạo ra các ranh giới, nhằm phân tách các element, class, module..etc
    

    Cùng xem xét ví dụ sau:

    DisplayHelper – static function

    Alt

    ZxingDecoder cần lấy ra orientation device, hàm lấy orientation dùng ở rất nhiều nơi nên tạo sẵn một class DisplayHelper để dùng. Hàm get orientation cần context nên parameter của ZxingDecoder là context

    Display – interface

    Alt

    Mình tạo ra 1 interface là Display để có thể get orientation của device, parameter đầu vào của ZxingDecoder là display.

    Điểm khác biệt lớn nhất là ở cách viết 2 – ZxingDecoder đã tách khỏi(1 phần tách khỏi) framework android – khi nó xóa đi sự hiện diện của context, tức là mình đang cố gắng phân rõ ranh giới của ZxingDecoder khỏi android framework Nói một cách khác mình đang cố gắng decouple ZxingDecoder khỏi android framework

    ZxingDecoder là 1 bộ decode barcode – nó ko phụ thuộc vào framwork, việc decode này mang ý nghĩa – class mình viết có thể chạy được ở bất cứ đâu, không chỉ là trên android framework

    Ngoài ra, nếu sử dụng DisplayHelper – khi có càng nhiều nơi, càng nhiều class, layer, module gọi hàm get orientation, hoặc một function của DisplayHelper thì các bạn có thể tượng tượng rằng DisplayHelper như một sợi xích đâm xuyên tất cả các layer, module, class, và buộc chặt các thành phần này lại với nhau và hoàn toàn không thể tách rời. Nói cách khác, khi đó ko thể tạo ra các ranh giới Boundaries cho bất cứ thành phần nào dùng chung DisplayHelper. Tất cả đều phẳng, và dính chặt vào android framework thông qua context của DisplayHelper

    Dễ code(Create) – khác với việc dễ maintain(Update).

    Phân ranh giới. (Boundaries) – là target cho hầu hết các task refactor bạn phải làm

    Làm thế nào để phân ranh giới? à, cái ý mình ko dám nói 😀 (ở bài viết này)

  • 5 cấp độ của làm việc từ xa – và tại sao bạn có thể đang ở cấp độ 2

    5 cấp độ của làm việc từ xa – và tại sao bạn có thể đang ở cấp độ 2

    Bài viết được đăng trên Medium tuần trước, đọc thấy có nhiều thông tin hữu ích nên dịch để giới thiệu với mọi người trong thời kỳ mô hình WFH – Work From Hearts đang lên ngôi <3

    COVID-19 đã buộc các công ty trên toàn thế giới phải ban hành – hoặc tạo ra – các phương thức làm việc từ xa. Các công ty như Box, Amazon, Airbnb, Facebook, Google và Microsoft đều đã và đang thử nghiệm với nhân viên của họ một số biến thể của hình thức làm việc từ xa.

    Giờ đây, các tổ chức truyền thống ở các lĩnh vực như bất động sản, kế toán và chính quyền địa phương cũng bắt đầu với hình thức này.
    Các cuộc gọi nhóm video call trên Zoom, như trong tấm ảnh dưới đây, được thực hiện bởi một số nhóm làm việc vừa bắt đầu làm quen với hình thức làm việc từ xa, đang tràn ngập trên Twitter và LinkedIn cũng như Facebook.

    Tuy nhiên, cũng giống như các công việc có giá trị, luôn có nhiều mức độ thành thạo và tinh tế khác nhau tương ứng với từng quy mô của nó.
    Nhiều nhân viên lần đầu làm việc từ xa coi đó đơn giản là tải xuống Zoom, Slack và truy cập vào email, rồi coi đó là cách giải quyết trọn vẹn cho hình thức làm việc từ xa.
    Nhưng chiếc áo không làm nên thầy tu – còn rất nhiều điều quan trọng khác mà chúng ta cần quan tâm khi bắt đầu với hình thức làm việc từ xa.

    Công ty Automattic

    Nói đến đẳng cấp cao nhất trong hình thức làm việc từ xa, có rất ít công ty có thể làm điều đó tốt hơn Automattic – công ty đứng sau WordPress – nơi sở hữu khoảng 35% số trang web trên mạng Internet.

    Automattic – tại thời điểm viết bài – có 1.170 nhân viên nằm rải rác trên 75 quốc gia, nói 93 ngôn ngữ. Tự hào có mức định giá công ty là 3 tỷ đô la Mỹ, đã thực hiện một số thương vụ đáng chú ý như mua lại WooCommerce và nền tảng blog, Tumblr.

    Công ty này không có văn phòng, với các nhân viên hợp tác làm việc qua phương thức duy nhất là làm việc online.

    Matt Mullenweg

    Người sáng lập của Automattic, Matt Mullenweg (vì tên anh ta có 2 chữ “t”, nên tên công ty này cũng được nhân đôi số chữ “t” theo) gần đây đã xuất hiện trên kênh podcast Making Sense nổi tiếng của Sam Harris để nói về cái mà anh ta gọi là 5 cấp độ của các nhóm phân tán (anh ta thích dùng từ “phân tán” thay vì “từ xa” vì cụm từ “từ xa” kia ngụ ý rằng vẫn còn một nơi làm việc tập trung.)

    Những ý kiến của Mullenweg thật đáng khích lệ (ít nhất là đối với tôi), nó tương tự như những gì tôi đã nói với nhiều khách hàng và đối tác của mình – “Các công cụ chỉ tốt tương đương với cách bạn sử dụng chúng”. Trên thực tế, lạm dụng các công cụ thực sự có thể khiến chúng ta giảm năng suất.

    Bên dưới là cách giải thích và diễn giải của tôi về những ý kiến của Mullenweg về 5 cấp độ của làm việc từ xa.

    Năm cấp độ của làm việc từ xa

    Cấp 1: Hành động không có chủ ý

    Các công ty không có chủ ý hỗ trợ làm việc từ xa, tuy nhiên các nhân viên vẫn có thể tiếp tục một phần công việc nếu họ ở nhà trong một ngày.

    Họ có quyền truy cập vào điện thoại thông minh và email của mình. Và có lẽ họ cũng sẽ tham gia vào một vài cuộc họp.

    Nhưng rồi họ sẽ tạm gác hầu hết mọi việc cho đến khi họ trở lại văn phòng, rồi lại trở thành một cái bóng của chính bản thân mình trong văn phòng như thường lệ.

    Cấp 1 là nơi mà phần lớn các tổ chức & nhân viên đang làm việc trước khi có sự bùng phát của đại dịch COVID19.

    Cấp độ 2: Tái tạo lại văn phòng làm việc dưới hình thức trực tuyến

    Đây là nơi hầu hết các tổ chức hiện đang cư trú trong dịch COVID19 – đặc biệt là những tổ chức truyền thống.

    Đây là nơi nhân viên của bạn có quyền truy cập vào phần mềm họp trực tuyến (ví dụ: Zoom), phần mềm nhắn tin (ví dụ: Slack) và email, nhưng thay vì thiết kế lại công việc để tận dụng các phương tiện mới này, cuối cùng các nhóm sẽ xây dựng lại cách họ làm việc trong văn phòng như trước đây nhưng dưới hình thức trực tuyến.

    Điều này làm trầm trọng thêm nhiều thói quen xấu đã xâm nhập vào văn phòng hiện đại và ngăn chặn khả năng suy nghĩ của những người lao động tri thức – đó là khi bạn phải nhìn vào các cuộc gọi video 10 người trong khi chỉ cần hai người là đủ, hay hơn 60 lần gián đoạn mỗi ngày qua Slack và các cuộc gọi điện thoại, kiểm tra thông báo và trả lời email hơn 70 lần một ngày trong suốt cả ngày, hoặc là khả năng phản hồi nhanh được mong đợi đối với tất cả các nhân viên, khiến họ bị buộc dây với chiếc máy tính như một phản xạ có điều kiện.

    Mullenweg coi việc thiếu thiết kế lại công việc xung quanh các phương tiện mới tương tự như sự thiếu hiệu quả trong nhiều dự án chuyển đổi kỹ thuật số trị giá hàng triệu đô la, mà ở đó các quy trình thủ công bị lỗi và dư thừa được tạo ra trong những năm 1980 lại tiếp tục được số hóa một cách hiệu quả – nhưng chờ đã – chúng vẫn là các quy trình bị lỗi, và chúng bị dư thừa cơ mà?

    Ở cấp độ 2, mọi người vẫn được yêu cầu có mặt trực tuyến từ 9h sáng tới 5h chiều và nếu bạn ở cấp độ 2, bạn vẫn còn một chặng đường dài để đi.

    Cấp độ 3: Thích nghi với các công cụ mới

    Ở cấp độ 3, các tổ chức bắt đầu thích nghi và tận dụng lợi thế của các công cụ mới. Mullenweg nói đến các tài liệu được chia sẻ (chẳng hạn như qua Google Doc), được nhìn thấy bởi tất cả mọi người và cập nhật liên tục theo thời gian thực trong suốt cuộc thảo luận, từ đó có được sự hiểu biết chung về những gì được thảo luận cũng như được quyết định, giúp loại bỏ nguy cơ bị lãng phí thời gian khi có sự hiểu nhầm về nội dung muốn truyền đạt.

    Ở giai đoạn này, các công ty cũng bắt đầu đầu tư vào các thiết bị tốt hơn cho nhân viên của mình, chẳng hạn như công cụ hỗ trợ cho các cuộc gọi video như webcam và micro chống ồn.

    Giao tiếp bằng văn bản hiệu quả trở nên cấp thiết khi các công ty muốn ủng hộ cho hình thức làm việc từ xa. Khó chịu với việc phải tham gia vào những cuộc gọi bất chợt, và ưu tiên cho giao tiếp bất đồng bộ (sẽ được nói đến chi tiết hơn ở phần tiếp theo), hầu hết các giao tiếp tại Automattic dựa trên nền tảng văn bản, và do đó phối hợp ăn khớp một cách nhịp nhàng và đúng lúc trở thành yếu tố quyết định.

    Trên thực tế, Mullenweg chia sẻ rằng hầu hết các công việc tuyển dụng ở công ty ông được thực hiện qua văn bản thay vì các cuộc gọi điện thoại hoặc cuộc gọi video cho ứng viên.

    Khi nhắc đến các cuộc họp:

    • Chỉ tổ chức một cuộc họp nếu điều đó là hoàn toàn cần thiết và không thể đạt được kết quả tương tự bằng hình thức cuộc trò chuyện nhanh, cuộc gọi điện thoại, email, văn bản hoặc tin nhắn.
    • Giới hạn thời gian mặc định cho cuộc họp là 15 phút và chỉ kéo dài thời gian nếu thực sự cần thiết (cuộc họp càng ngắn, bạn sẽ càng phải trao đổi ngắn gọn và rõ ràng, hạn chế được thời gian nói chuyện lan man vô nghĩa).
    • Đặt một lịch trình họp cụ thể và kết quả mong muốn trước khi thực hiện cuộc họp.
    • Chỉ mời những người bắt buộc phải có (trừ khi là những quyết định lớn cần nhiều người tham gia, còn thường thì hai người sẽ đưa ra được quyết định trong khi ba người lại hiếm khi).
    • Đồng ý về các bước triển khai tiếp theo, phân bổ người có trách nhiệm và đặt ngày đến hạn (điều này đặc biệt quan trọng để tránh các cuộc họp boomerang – họp xong như chưa họp).
    • Không bao giờ, sử dụng một cuộc họp chỉ đơn giản là để truyền đạt thông tin – đó là những gì mà email hoặc tin nhắn được thiết kế để giải quyết. Nhiều người thực sự đang thấy rằng hầu hết các cuộc họp này thực tế có thể giải quyết qua email.

    Cấp độ 4: Giao tiếp bất đồng bộ

    “Tôi sẽ đụng tới nó cho tới lúc thích hợp.” –  Đây là bản chất của giao tiếp bất đồng bộ.

    Thực tế là hầu hết mọi thứ không đòi hỏi phải phản ứng lại ngay lập tức. Đối với hầu hết mọi thứ chẳng hạn như thư điện tử hay tin nhắn tức thời nên thực hiện công việc của nó là truyền đạt thông tin, còn người nhận sẽ trả lời khi đến thời điểm phù hợp với họ.

    Nếu một cái gì đó thực sự khẩn cấp, thì phương thức giao tiếp sẽ phản ánh điều đó. Nhấc điện thoại, hoặc vỗ vai người muốn gọi – nhưng chỉ khi đó là việc thực sự khẩn cấp.

    Bên cạnh lợi ích rõ ràng và to lớn của việc dành cho những người lao động tri thức có thời gian suy nghĩ, sáng tạo và đi vào trạng thái dòng chảy (một trạng thái tâm lý theo đó chúng ta có năng suất cao hơn năm lần theo McKinsey), giao tiếp bất đồng bộ còn khiến mọi người đưa ra quyết định tốt hơn.

    Như Robert Greene nói: nếu bạn muốn cắt cảm xúc ra khỏi phương trình, hãy tăng thời gian phản hồi của bạn. Cho mọi người thời gian để suy nghĩ giữa câu hỏi và câu trả lời, thay vì trở thành nạn nhân của việc luôn phải thốt ra điều đầu tiên xuất hiện trong đầu ở các cuộc họp hay khi bị vỗ vai, sẽ mang lại lợi ích chung cho tổ chức về lâu dài.

    Để tránh việc chuyền qua chuyền lại cũng như mất nhiều thời gian trao đổi, hãy chắc chắn rằng các thông điệp bất đồng bộ luôn đảm bảo:

    • Cung cấp đầy đủ và chi tiết bối cảnh của tình huống, cũng như cung cấp các hành động cần thiết một cách rõ ràng đi kèm với các kết quả mong muốn.
    • Cung cấp thông tin về ngày cần hoàn thành.
    • Cung cấp cách thức liên hệ trong trường hợp người nhận cần thêm thông tin hoặc không thể đáp ứng yêu cầu của bạn.

    Ví dụ:

        “Chào An,

        Kèm theo là tài liệu hợp nhất cho công ty mới được tách ra từ công ty hiện tại của chúng tôi.

        Vui lòng ký vào tài liệu khi được yêu cầu và gửi lại cho tôi trước 4 giờ chiều thứ Sáu tuần này.

        Nếu bạn có bất kỳ mối quan tâm nào, hãy gọi cho tôi vào số 555 1983.”

    Các công ty thực sự thực hiện giao tiếp bất đồng bộ đã vượt qua cuộc cách mạng công nghiệp, và không còn giới thiệu sự hiện diện của mình thông qua năng suất, hoặc sản lượng hàng giờ, như các nhà máy hiện tại đang làm.

    Mullenweg chỉ ra rằng các đội phân tán toàn cầu, những người làm việc bất đồng bộ và thành thạo với cách làm việc kiểu chạy tiếp sức, có thể hoàn thành lượng việc cao gấp ba lần so với một nhóm làm việc với các thành viên ở trong văn phòng từ 9 giờ sáng đến 5 giờ chiều.

    Đánh thức con cú đêm trong bạn

    Một yếu tố chưa được bàn tới trong các cuộc trao đổi đó là nhịp sinh học.

    Khoa học cho thấy các mô hình ngủ phổ biến của chúng ta – các kiểu nhịp sinh học – được lập trình từ khi sinh. Mọi người hoặc là cú đêm hoặc sẽ là chim sâu buổi sớm.

    Nhà vật lý thiên văn Sabrina Stierwalt đã viết cho Science American rằng:

    Sở thích của chúng ta đối với cái này hay cái khác được mã hóa trong các gen được gọi là gen “đồng hồ hoặc gen “chu kỳ” qua đó điều chỉnh nhịp sinh học của chúng ta, và liên quan trực tiếp đến huyết áp, sự trao đổi chất, nhiệt độ cơ thể cũng như mức độ hormone.

    Một số nghiên cứu đã phát hiện ra rằng khoảng 30 đến 40% dân số là những con cú đêm, điều đó có nghĩa là ngày làm việc 9h đến 5h hiện đại đang phá hoại những nỗ lực sáng tạo và trí tuệ của gần một nửa lực lượng lao động.

    Stierwalt nói rằng ngày làm việc thường bắt đầu từ 7 đến 9 giờ sáng. Tuy nhiên, những con cú đêm có thể gặp hiện tượng “lệch múi giờ do tác động xã hội” nếu họ thức dậy sớm thế này – nghĩa là, họ có thể cảm thấy hiện tượng tương tự như hiện tượng chúng ta đã gặp sau một chuyến bay qua đêm. Những người dậy sớm thường ít gặp phải tình trạng lệch múi giờ này, điều này giúp họ có lợi thế hơn những con cú đêm.

    Các nghiên cứu cho thấy rằng trong khi những người dậy sớm tỉnh táo hơn vào buổi sáng, thì cú đêm cho thấy sự tập trung mạnh mẽ hơn và sự chú ý kéo dài hơn 10 giờ sau khi thức dậy so với đồng loại “chim sâu buổi sớm” của họ.

    Các công ty bất đồng bộ cung cấp cho cú đêm sự linh hoạt trong việc bắt đầu ngày làm việc mới muộn hơn, miễn là phải đảm bảo có những khoảng thời gian mà họ và đồng nghiệp khác cùng làm việc với nhau.

    Và như Mullenweg chia sẻ: Công ty Automattic đang ở cấp 4

    Cấp 5: Niết Bàn (Nirvana)

    Đây là nơi nhóm phân tán của bạn hoạt động tốt hơn bất kỳ nhóm làm việc trực tiếp tại chỗ nào từng có thể. Mullenweg đánh đồng mức độ này với việc nhấn mạnh hơn vào thiết kế môi trường, trong đó luôn có sự quan tâm đến văn hóa tổ chức, và môi trường vật lý mà mọi người đang làm việc.

    Nhược điểm

    Tất nhiên, có những ưu và nhược điểm với hầu hết các quyết định.

    Có thể tìm thấy ba nhược điểm hoặc mối lo ngại lớn đối với các đội mới làm việc từ xa và cách đối phó với chúng dưới đây:

    Liên kết đội và xây dựng

    Thay vì nói với nhân viên của họ ở văn phòng 11 tháng một năm và được nghỉ 4 tuần, Automattic lật ngược lại kịch bản này: “Nhân viên có 11 tháng làm việc từ xa một năm và phải dành thời gian để đi lại tới 4 tuần một năm cho các sự kiện gắn kết và xây dựng đội nhóm.”

    Họ cũng sử dụng các ứng dụng được xây dựng tùy chỉnh để theo dõi ai đã gặp ai, và sau đó chỉ định chỗ ngồi, nói tại một bữa tiệc tối, để mọi người ngồi với những người mà họ chưa gặp trước đó.

    Sự thấu hiểu và giao tiếp trong văn phòng

    Với tất cả mọi người làm việc trực tuyến, bạn bỏ lỡ các cuộc trò chuyện tán gẫu khi chạm mặt nhau ở quầy ăn/cây lấy nước, hay tình cờ nghe thấy người khác nói về điều mà bạn có thể giúp đỡ, hoặc có ngay nhận thức chung về các hoạt động của nhóm nhờ việc đứng từ xa lắng nghe những cuộc trao đổi.

    Để giải quyết sự thiếu hụt này, Automattic sử dụng một plugin WordPress có tên P2, hoạt động như một blog nội bộ và là nơi có một số lượng đáng kể các cuộc trò chuyện và hoạt động được ghi lại.

    Sự an toàn

    Mullenweg nói đến sự an toàn với phương thức bảo mật đầu cuối sử dụng trong hình thức BYOD – sử dụng thiết bị cá nhân như máy tính xách tay và điện thoại thông minh.

    Tuy nhiên, thay vì nhấn mạnh hay tập trung quá mức đến sự kiểm soát truy cập, chúng ta cần được bảo vệ để chống lại các hành vi độc hại. Với hơn 70% các vụ hack CNTT sử dụng kỹ thuật Social Engineering – kỹ thuật thao túng hành vi con người để xâm nhập vào bên trong thay vì tập trung khai thác các lỗ hổng bảo mật của máy móc, thiết bị.

    “Hiện tại, làm việc tại nhà là một đặc quyền và nó không phải là quyền của nhiều người, vì vậy chúng ta hãy chú ý hơn về cách thức mình làm việc, hãy chứng minh rằng chúng ta có thể làm việc hiệu quả hơn so với làm việc tại văn phòng qua đó chúng ta sẽ được cho phép làm việc từ bất cứ nơi nào chúng ta muốn thường xuyên hơn.”

  • The Good, The Bad and the Ugly

    (Một bộ phim kinh điển mà mình cực kỳ thích nên lấy nó làm title cho bài viết này)
    Đây là bản “hồi ký” khi mình tìm solution cho một bài toán, hi vọng nó sẽ giúp ai đó định hướng được đường phải đi.

    Bài toán: mix 2 file audio trên android mà không dùng thêm library nào

    • Suy nghĩ đầu tiên là tìm xem android có chìa API nào ra để mix file ko – bỏ đi, các cậu ko cần search, android ko chìa api nào ra để làm việc này đâu
    • Suy nghĩ thứ 2: Đưa dữ liệu âm thanh(mp3..etc) về dạng raw data và thử mix các bit vào nhau? có vẻ khả thi

    The bad:

    
    private byte[] mBufData1 = null;
    private byte[] mBufData2 = null;
    private ArrayList mBufMixedData = new ArrayList<>();
    
    private void loadMixedData() {
            int length1 = mBufData1.length;
            int length2 = mBufData2.length;
            int max = Math.max(length1, length2);
            int tempSplitSize;
            if (length1 == length2) {
                for (int i = 0; i < length1; i++) {
                    mBufMixedData.add((byte) (mBufData1[i] + mBufData2[i]));
                }
            } else {
                if (length2 > length1) {
                    tempSplitSize = length1;
                } else {
                    tempSplitSize = length2;
                }
                for (int i = 0; i < tempSplitSize; i++) {
                    mBufMixedData.add((byte) (mBufData1[i] + mBufData2[i]));
                }
                if (length2 > length1) {
                    for (int i = tempSplitSize; i < max; i++) {
                        mBufMixedData.add((mBufData2[i]));
                    }
                } else {
                    for (int i = tempSplitSize; i < max; i++) {
                        mBufMixedData.add((mBufData1[i]));
                    }
                }
    
            }
        }

    Chạy được thật, âm thanh đã được mix lại như file karaoke ngoài hàng. Nhưng nhìn source code thì chỉ có đứa mù dở mới ko nhìn thấy vấn đề OOM chắc chắn phát sinh.
    Nếu dừng ở đây – Ok, nó chạy được, chúng ta sẽ giấu đi đoạn OOM kia, kệ cho dự án hót shit vì còn lâu họ mới test ra issue ý, lúc phát hiện chúng ta đã cao chạy xa bay rồi. Ka ka ka
    Chúng ta là những kẻ tồi tệ

    The Ugly

    Cải tiến hơn 1 chút, để tránh OOM, mình ko load hết dữ liệu lên ram nữa mà đưa vào DataOutputStream để write từng bit xuống file

    private void createWaveMixing(String p1, String p2, String p3) throws IOException {
            int size1 = 0;
            int size2 = 0;
            int size1;
            int size2;
    
            FileInputStream fis1 = null;
            FileInputStream fis2 = null;
            try {
                fis1 = new FileInputStream(p1);
                fis2 = new FileInputStream(p2);
                size1 = fis1.available();
                size2 = fis2.available();
                long totalAudioLen = size1;
                if (size1 < size2) {
                    totalAudioLen = size2;
                }
                long totalDataLen = totalAudioLen + WavUtil.LENGTH_EXTENDED;
                long longSampleRate = WavUtil.getSampleRate(p1);//44100
                long totalDataLen = totalAudioLen + WavUtils.LENGTH_EXTENDED;
                long longSampleRate = MediaCodecUtils.getSampleRate(p1);//44100
                int channels = 2;
                long byteRate = WavUtil.RECORDER_BPP * longSampleRate * channels / 8;
                long byteRate = WavUtils.RECORDER_BPP * longSampleRate * channels / 8;
                DataOutputStream out = null;
                try {
                    out = new DataOutputStream(new FileOutputStream(p3));
                    WavUtil.writeWaveFileHeader(out, totalAudioLen, totalDataLen, longSampleRate, channels, byteRate);
                    WavUtils.writeWaveFileHeader(out, totalAudioLen, totalDataLen, longSampleRate, channels, byteRate);
                    out.write(toByteArray(mBufMixedData));
                } catch (Exception e) {
                    Log.e(TAG, "#createWaveMixing():" + e.getMessage(), e);
                } finally {
                    if (out != null) {
                        out.close();
                    }
                }
            } finally {
                if (fis1 != null) {
                    fis1.close();
                }
                if (fis2 != null) {
                    fis2.close();
                }
            }
    
        }
    
        private byte[] toByteArray(ArrayList in) {
            byte[] data = new byte[in.size()];
            for (int i = 0; i < data.length; i++) {
                data[i] = in.get(i);
            }
            return data;
        }
    }

    Cách làm này tránh được OOM, nhưng phải nói là nó chậm, thực sự chậm, chậm kinh khủng. Đâu đó mất khoảng 50 -60 s cho 2 file music raw dài 3 phút.
    Nói chung là chạy được, ko có issue gì cả, chỉ chậm thôi. Chậm thì tự tìm cách mà improve đi, kêu gì – đúng ko các cậu? – lại chả phải quá.
    Nếu dừng ở đây, chúng ta là những gã lừa đảo.

    Nhưng dù sao mình cũng là người vừa đẹp trai lại tốt tính, nên mới xuất hiện tình huống thứ 3

    The Good

    Lần này mình optimize bằng cách sử dụng FileChannel, thay vì handle từng bit một, thì mình bóc một nhóm lớn ra để mix(buôn sỉ mới nhanh giầu)

    private void createWaveMixing(String p1, String p2, String p3) throws IOException {
            int size1;
            int size2;
    
            FileInputStream fis1 = null;
            FileInputStream fis2 = null;
            try {
                fis1 = new FileInputStream(p1);
                fis2 = new FileInputStream(p2);
                size1 = fis1.available();
                size2 = fis2.available();
                long totalAudioLen = size1;
                if (size1 < size2) {
                    totalAudioLen = size2;
                }
                long totalDataLen = totalAudioLen + WavUtils.LENGTH_EXTENDED;
                long longSampleRate = MediaExtractorUtils.getSampleRate(p1);//44100
                int channels = 2;
                long byteRate = WavUtils.RECORDER_BPP * longSampleRate * channels / 8;
                DataOutputStream out = null;
    
                FileChannel fc1 = fis1.getChannel();
                FileChannel fc2 = fis2.getChannel();
                long length1 = fc1.size();
                long length2 = fc2.size();
                try {
                    out = new DataOutputStream(new FileOutputStream(p3));
                    WavUtils.writeWaveFileHeader(out, totalAudioLen, totalDataLen, longSampleRate, channels, byteRate);
                    {
                        ByteBuffer buff1 = ByteBuffer.allocate(BUFFER_SIZE);
                        ByteBuffer buff2 = ByteBuffer.allocate(BUFFER_SIZE);
    
                        ByteBuffer mixedBuffer = null;
                        if (length1 == length2) {
                            while (fc1.read(buff1) > 0) {
                                fc2.read(buff2);
                                mixedBuffer = mixByteBuffer(buff1, buff2);
                                out.write(mixedBuffer.array());
                                buff1.clear();
                                buff2.clear();
                                mixedBuffer.clear();
                            }
                            if (mixedBuffer != null) {
                                mixedBuffer.clear();
                            }
                        } else {
                            if (length2 > length1) {
                                while (fc1.read(buff1) > 0) {
                                    fc2.read(buff2);
                                    mixedBuffer = mixByteBuffer(buff1, buff2);
                                    out.write(mixedBuffer.array());
                                    buff1.clear();
                                    buff2.clear();
                                    mixedBuffer.clear();
                                }
                                while (fc2.read(buff2) > 0) {
                                    out.write(buff2.array());
                                    buff2.clear();
                                }
                            } else {
                                while (fc2.read(buff2) > 0) {
                                    fc1.read(buff1);
                                    mixedBuffer = mixByteBuffer(buff1, buff2);
                                    out.write(mixedBuffer.array());
                                    buff1.clear();
                                    buff2.clear();
                                    mixedBuffer.clear();
                                }
                                while (fc1.read(buff1) > 0) {
                                    out.write(buff1.array());
                                    buff1.clear();
                                }
                            }
    
                        }
    
                    }
                } catch (Exception e) {
                    Log.e("test", "#createWaveMixing():" + e.getMessage(), e);
                } finally {
                    if (out != null) {
                        out.close();
                    }
                    fc1.close();
                    fc2.close();
                }
            } finally {
                if (fis1 != null) {
                    fis1.close();
                }
                if (fis2 != null) {
                    fis2.close();
                }
            }
    
        }

    Kết quả tốc độ tăng gấp 6-8 lần mà lại ko có issue gì cả
    Lần này(dường như) mình sẽ có thể là người tốt

    Cùng 1 bài toán, sẽ có các cách giải khác nhau
    Khoảng thời gian cho phép khác nhau, chúng ta cũng sẽ phải chọn các giải pháp khác nhau(chấp nhận được, chạy có issue, chậm…etc)

    1 ngày làm được ko? được – the bad
    1 tuần làm được ko? được – the ugly
    1 tháng làm được ko? được – the good

    Cái gì cũng sẽ có giá của nó, tùy vào deadline, tùy vào thời điểm, chúng ta hãy cố chọn ra được cách làm tốt nhất

    1 phút dành cho quảng cáo, GDK đã có 1 phiên bản có thể record, mix, nối audio nhé, anh em có thể search gdk-soundutilities

  • Kiểm thử tự động cùng Robot Framework dành cho tester

    I. Giới thiệu

    1. Tổng quan về Robot Framework

    Robot Framework là một testing framework. Nó cung cấp mọi thứ cần thiết để xây dựng và phát triển một kịch bản kiểm thử, gồm điều kiện đầu vào/kết thúc, báo cáo kết quả, … Điểm hấp dẫn của Robot Framework với các tester chính là chúng ta không cần quan tâm đến các thuật toán lập trình cơ bản nhất. Mọi thứ chúng ta cần làm chính là viết ra một kịch bản kiểm thử dựa trên các từ khóa (keyword) mà thôi.

    Cụ thể hơn, Robot Framework là:

    • Framework dùng để kiểm thử, cung cấp nền tảng kiểm thử cho tester dựa trên ngôn ngữ lập trình Python. Cách tiếp cận của nền tảng kiểm thử này là hướng từ khoá (keyword driven) và hướng dữ liệu (data driven) dành cho việc kiểm thử để nghiệm thu sản phẩm ngay từ đầu (end-to-end acceptance testing).
    • Để tiếp cận nền tảng kiểm thử này, tester chỉ cần viết kịch bản kiểm thử theo hướng từ khóa (keyword driven) và hướng dữ liệu (data driven).
    • Tester có thể tạo các từ khóa cấp cao mới từ những cái hiện có bằng cách sử dụng cú pháp tương tự được sử dụng để tạo ra các trường hợp thử nghiệm.

    Các tính năng nổi bật của Robot Framework:Những việc làm hấp dẫn

    • Robot Framework giúp chúng ta thực hiện kiểm thử tự động với kịch bản ở dạng bảng một cách dễ dàng. Robot Framework đưa ra kết quả thực thi các kịch bản kiểm thử và các log ở dạng html, giúp chúng ta đọc và phân tích kết quả nhanh chóng và dễ dàng hơn.
    • Robot Framework có hỗ trợ chức năng đánh dấu các kịch bản kiểm thử, cho phép chúng ta lựa chọn kịch bản kiểm thử tiện lợi và nhanh chóng.
    • Thế mạnh lớn nhất của Robot Framework chính là khả năng chạy trên nhiều hệ điều hành khác nhau mà không cần chỉnh sửa kịch bản kiểm thử hay các từ khóa ở tầng dưới.

    2. Các thư viện hỗ trợ trong Robot Framework

    Robot Framework có rất nhiều thư viện hỗ trợ cho việc kiểm thử tự động, có thể tham khảo các thư viện dành cho Robot Framwwork tại http://robotframework.org/#test-libraries .

    7c09fc7530966020cdd4ebce92806688bee90a11

    Tuy nhiên, trong nội dung bài viết này, chúng tôi sẽ tập trung giới thiệu 2 thư viện phổ biến nhất đó là Selenium2Library và Calculator Library.

    2.1 Selenium2Library

    • Selenium2Library được sử dụng để kiểm thử trên nền Web, và được fork từ SeleniumLibrary và được bổ sung để sử dụng Selenium 2 và WebDriver.
    • Selenium2Library hoạt động ở hầu hết các trình duyệt hay dùng như IE, Firefox, Safari, Chrome, … và có thể được dùng với cả Python và Jython.

    Để chạy các testcase bằng cách sử dụng Selenium2Library, trước tiên bạn cần:

    1. Cài đặt Selenium2Library,
    2. Import Selenium2Library vào các testsuite Robot.
    3. Dùng từ khóa Open Browser để bật trình duyệt muốn dùng kiểm thử.

    Tại sao nên sử dụng thư viện Selenium2Library?

    Selenium2Library là ngôn ngữ rất sát với ngôn ngữ thực tế của người dùng, bạn mong muốn action gì bạn chỉ cần gõ từ khóa tương ứng.

    Ví dụ:

    • input text: nhập chuỗi ký tự
    • click button: nhấp chuột
    • double click element: nhấp đôi chuột vào element
    • get alert message: lấy giá trị của thông báo
    • open context menu: mở các menu con
    • v.v…

    Tìm hiểu thêm tại:

    2.2 CalculatorLibrary

    Tiếp theo ta sẽ tìm hiểu về một thư viện được có sẵn trong Robot Framework, một thư viện về tính toán đơn giản, nó chỉ chứa logic nghiệp vụ chứ không bao gồm phần UI.

    Chúng ta cùng đi qua một ví dụ về CalculatorLibrary và cùng run một test case đơn giản để có thể hiểu hơn về Robot Framework.

    Bạn có thể tải bản demo tại đây: https://bitbucket.org/robotframework/robotdemo/downloads.

    Ví dụ:

    b94e442c87eff8f32e9e0c55418f3c5f7c165569

    Ví dụ trên bao gồm 5 Testcases: Trong đó có 4 TCs ví dụ trường hợp PASSED, và 1 TC ví dụ về trường hợp FAILED:

    • Push button PASSED
    • Push multiple buttons FAILED
    • Simple calculation PASSED
    • Longer calculation PASSED
    • Clear PASSED

    Các keyword thì khá đơn giản và dễ hiều nên mình không giải thích gì thêm. Bây giờ chúng ta sẽ chạy thử 5 TCs này bằng Terminal trên Linux như sau:

    1. Download thư viện và có sẵn tại: https://bitbucket.org/robotframework/robotdemo/downloads.
    2. Mở terminal và trỏ đến folder chứa file vừa download.
    3. Run Test case “keyword_driven.robot” bằng command sau: $ robot keyword_driven.robot.
    a5fabe04a9d9d31297524951b7ba35c005bd9856

    Kết quả là 4 TCs Pass và 1 TC Failed.

    Kết luận: Đối với Robot Framework chúng ta không cần phải biết lập trình để viết testcase và script như những công cụ khác. Đối với những yêu cầu đơn giản và nhanh chóng thì Robot Framework là một sự lựa chọn phù hợp.

    II. Cài đặt

    Phần này sẽ hướng dẫn cách cài đặt RF cùng với Selenium trên Linux và Windows.

    1. Cài đặt Robot Framework

    Bước 1. Cài đặt Python.

    Trước hết, vì Robot Framework là một nền tảng kiểm thử dựa trên nền tảng Python, nên trước tiên cần cài đặt Python (nên cài Python 2.5 hoặc mới hơn – khuyến cáo cài đặt Python 2.7).

    Linux: Python thường đi kèm với cài đặt Ubuntu/Linux. Để kiểm tra xem Python đã cài đặt chưa, cũng như phiên bản của nó, dùng câu lệnh sau trên Terminal (có thể dùng phím tắt Ctrl+Alt+T để bật Terminal: $ python –version. Nếu Python đã được cài đặt, bạn sẽ nhìn thấy phiên bản của nó, chẳng hạn Python 2.7.6.

    Windows: Tương tự như trên Linux, bạn hãy bật cmd lên và kiểm tra xem python đã được cài đặt chưa bằng lệnh: python –version. Nếu chưa, đi đến https://www.python.org/ và tải phiên bản Python tương ứng và cài đặt nó.

    Bước 2. Cài đặt PIP (Python Package Manager).

    Linux: PIP là một package manager cho việc thiết lập các gói Python. Để cài đặt PIP, dùng câu lệnh sau:

    $ wget https://bootstrap.pypa.io/get-pip.py

    $ sudo python get-pip.py

    Windows: PIP đã được cài đặt nếu bạn đang dùng phiên bản Python 2 >=2.7.9 hoặc Python 3 >=3.4 tải xuống từ https://www.python.org/, tuy nhiên bạn sẽ cần nâng cấp PIP bằng lệnh sau:

    python -m pip install -U pip

    Bước 3. Cài đặt gói Robot Framework bằng cách sử dụng PIP.

    Linux: Dùng câu lệnh sau để cài đặt Robot Framework:

    $ sudo pip install robotframework

    Phiên bản mới nhất của Robot Framework sẽ được cài tự động. Nếu muốn cài một phiên bản cụ thể, chỉ việc thêm vào, chẳng hạn:

    $ sudo pip install robotframework==2.8.4

    Sau khi cài đặt hoàn tất, dùng câu lệnh sau để xem việc cài đặt đã thành công chưa: $ pybot –version. Bạn sẽ nhìn thấy thông tin phiên bản Robot Framework nếu thiết lập thành công.

    Windows: Tại cửa sổ Command promt, chuyển tới thư mục cài đặt Python và dùng lệnh sau để cài đặt RF:

    pip install robotframework

    Sau khi cài đặt thành công, dùng pybot –version để kiểm tra:

    85a54dbbe7e47d670d9649022a42a348851f4d99

    2. Cài đặt Selenium2Library

    Để làm việc với Webdriver (Selenium2) và Robot Framework, bạn cần cài đặt Selenium2Library bằng cách sử dụng PIP:

    Linux: Dùng câu lệnh sau: $ sudo pip install robotframework-selenium2library

    Câu lệnh này sẽ tự động cài các dependency của nó, gồm decorator, và các gói Selenium. Ngay sau khi hoàn tất cài đặt, dùng câu lệnh $ python để chuyển đến cửa sổ của python:

    086ea3973c728c0a8dc436a8df6705c44611876a

    Ở cửa sổ này, gõ câu lệnh sau để import Selenium2 Library: >> import Selenium2Library.

    Nếu không thấy lỗi nào bắn ra nghĩa là thư viện Selenium2 đã được cài thành công. Nếu muốn thoát khỏi cửa sổ Python, dùng lệnh exit().

    Windows: Tại cửa sổ Command promt, dùng lệnh: pip install robotframework-selenium2library. Sau khi cài đặt thành công sẽ có thông báo như sau:

    e0077af87537efcafad673aec2cde3c38638274d

    3. Cài đặt RIDE (Standalone RobotFramework Test Data Editor)

    RIDE là một IDE để xây dựng kiểm thử bằng cách sử dụng Robot Framework. Ngoài RIDE ra, bạn có thể thay thế bằng SublimeText, IntelliJ hay Eclipse, … Vì RIDE được phát triển bằng cách sử dụng wxPython nên bạn cần cài bộ tool wxPython 2.8 có hỗ trợ unicode để chạy RIDE. Cụ thể như sau:

    Linux: Dùng câu lệnh sau để cài wxPython:

    $ sudo apt-get install python-wxgtk2.8

    $ sudo apt-get install python-wxversion

    Tiếp theo, dùng câu lệnh sau để cài RIDE:

    $ sudo pip install robotframework-ride

    Để xác minh xem việc cài đặt đã OK chưa, chạy câu lệnh sau: $ ride.py. Ứng dụng RIDE sẽ bật lên như sau:

    072281cc7ba1c61ffa0c24a1f95ea0756e379105

    Windows:

    c92c89abad3eba6ebfaac1b799ae6387919069e0
    3a74e904945698ffa13d88086b6bd922e79fc89d
    • Tại cửa sổ Command promt, dùng lệnh: pip install robotframework-ride để cài đặt RIDE.
    • Sau khi cài đặt thành công sẽ có thông báo như sau
    eb3219bf61baabb75d7a4a35b0a5111ebea0097c
    • Dùng lệnh: ride.py để khởi động RIDE.

    III. Cách bắt phần tử giao diện

    1. Tổng quan XPath

    • XPath là một cách để phân tích mã HTML nhằm xác định các yếu tố của một web driver.
    • Là ngôn ngữ hỗ trợ tìm kiếm thông tin trong tài liệu XML qua việc sử dụng biểu thức XPath để định hướng tìm kiếm dữ liệu trên XML thay vì phải thực hiện tìm kiếm đệ qui để duyệt cây XML.
    • Xpath định nghĩa 7 loại nodes theo mô hình thể hiện bên dưới từ root, element, attribute, text, namespace, processing-instruction và comment.
    • Ngoài ra, Xpath còn định nghĩa một số node đặc biệt để thể hiện mối quan hệ giữa các node trong mô hình trong quá trình xử lý như sau:
    1. Parent Node: node trên trực tiếp của node hiện hành.
    2. Child Node: tập node trực tiếp của node hiện hành cấp thấp hơn.
    3. Sibling: node ngang hàng hay cùng cha với node hiện hành.
    4. Ancestors: tất cả node con bên trên node hiện hành cùng nhánh.
    5. Descendants: tất cả node con bên dưới của node hiện hành cùng nhánh.

    Cú pháp của XPath:

    • Để truy vấn với đường dẫn tuyệt đối nghĩa là đi từ root của tài liệu XML đến các thành phần cần truy cập, XPath qui định với cú pháp bắt đầu bằng dấu /
    • Để truy vấn với đường dận tương đối để có thể truy cập đến thành phần bất kỳ thỏa điều kiện, XPath qui định cú pháp sử dụng với dấu //
    • Để truy vấn đến một thành phần bất kỳ mà không cần biết tên của nó là gì, XPath qui định ký tự sử dụng là *.
    • Để truy cập thuộc tính của một node, XPath qui định thuộc tính truy vấn phải có cú pháp bắt đầu là @.Ví dụ @tênThuộcTính.
    • Điều kiện khi truy vấn được đặt trong dấu []
    • Truy vấn lựa chọn nodes
    Biểu thứcĐịnh nghĩa
    tênNodeChọn tất cả các node con của tênNode.
    /Chọn tất cả các node tính từ root.
    //Chọn tất cả node tính từ node hiện hành.
    .Chọn node hiện hành.
    ..Chọn node cha của node hiện hành.

    Các phép toán được sử dụng trong XPath:

    • Đại số: +, -, * (nhân), div (chia thập phân), mod (chia lấy dư)
    • So Sánh hay quan hệ: =, != (khác), <, <=, >, >=
    • Luận lý: true, false, and, or, not
    • Kết hợp: | (hội)

    2. Cách bắt XPath bằng Firebug và FirePath

    Firebug và FirePath là 2 add-ons hỗ trợ cho việc bắt XPath nhanh và dễ dàng hơn trên Firefox browser.

    Cài đặt Firebug và FirePath:

    1. Trên Firefox browser, chọn icon [Open Menu] -> Add-ons.
    2. Tìm và cài đặt Firebug, FirePath.

    Sau khi cài đặt thành công, Firebug và FirePath xuất hiện trong mục Extensions và các icon của chúng sẽ xuất hiện trên thanh công cụ của Firefox.

    2b510b64cc5c74744898385a5d3c4895ab138f7f

    Sử dụng Firebug và FirePath:

    • Nhấp vào icon con bọ -> chọn thẻ FirePath.
    • Nhấp vào ký hiệu con mũi tên bên cạnh con bọ -> tiếp nhấp chuột vào element cần lấy xpath. xpath của element đó sẽ hiển thị:
    d1fbc9c3854ef35c3353076e1ba16bd7c621a5da

    Lưu ý:

    • XPath lấy được từ FirePath chỉ mang tính chất tham khảo và giúp người dùng xác định phần tử dễ dàng hơn.
    • XPath lấy được từ FirePath là cách đơn giản nhất nhưng lại chưa đảm bảo tính ổn định và duy nhất khi version của web page thay đổi. Vậy nên, người dùng có thể sử dụng một số cách hỗ trợ truy vấn sau để bắt XPath nhằm tăng tính ổn định và khả dụng khi muốn sử dụng các element liên quan đến nhau một cách chính xác hơn:
    AxisĐịnh nghĩa
    ancestorChọn tất cả các node trên của node hiện hành.
    ancestor-or-selfChọn tất cả các node trên của node hiện hành và chính nó.
    attributeChọn tất cả các thuộc tính của node hiện hành.
    childChọn node con của node hiện hành.
    descendantChọn tất cả các node dưới của node hiện hành.
    descendant-or-selfChọn tất cả các node dưới của node hiện hành và chính nó.
    followingChọn tất cả các node sau khi tag đóng của node hiện hành.
    following-siblingChọn tất cả các node ngang cấp sau khi tag đóng của node hiện hành.
    namespaceChọn tất cả namespace của node hiện hành.
    parentChọn tất cả node cha của node hiện hành.
    precedingChọn tất cả các thành phần trước khi bắt đầu tag mở của node hiện hành.
    preceding-siblingChọn tất cả các node ngang hàng trước khi bắt đầu tag mở của node hiện hành.
    selfChọn node hiện hành.

    Ví dụ:

    Vd1: ancestor

    1. Hiển thị tất cả các thẻ cha có chứa thẻ div id=<”identifier-shown”>, ko bao gồm thẻ div id=<”identifer-shown”> được inspect từ trang Login Gmail.
    fb1b340719938f5b70b42e784bf1d152f32404ab
    1. Nếu chỉ muốn hiển thị thẻ cha được chỉ định.
    347ef0cedd9f6ff09d2cfba5d6a3db99e50b6898

    Vd2: ancestor-or-self

    Hiển thị các thẻ cha và cả thẻ div id=<”identifier-shown”>, có bao gồm thẻ div id=”<identifier-shown>” được inspect từ trang Login Gmail.

    278d8b8f2a7b923f6b3c28327e8823d4741457e2

    Vd3: attribute

    Chọn tất cả các attribute hiện hành của nút Next ở trang Login Gmail.

    b5ad5ea2eab13aed28dea354e9437a6a3032919b

    Vd4: child

    Chọn tất cả các thẻ con của thẻ div class=”input-wrapper focused”.

    6152b5513962f8991e981c2b0f2cbe8575b96257

    Vd5: descendant

    Chọn tất cả các thẻ con và cháu của thẻ div id=”input-wrapper focused”, ko bao gồm thẻ div id=”input-wrapper focused”.

    96aa0fe73cb7df41d982193eb3b52dc4bbc60eeb

    Vd6: descendant-or-self

    Chọn tất cả các thẻ con và cháu của thẻ div id=”input-wrapper focused”, bao gồm cả thẻ div id=”input-wrapper focused”.

    374b38776e68e15f57e36fd056c93f70f6f47757

    Vd7: following

    1. Hiển thị tất cả các thẻ sau thẻ đóng của thẻ div class=”identifier-wrapper focused”.
    f82c138b5de9639827b4a1a9230339c221f390ba
    1. Hiển thị 1 thẻ sau thẻ đóng của thẻ div class=”identifier-wrapper focused”.
    9566b8874419e55d44286a49173ddf7cd713a883

    Vd8: following-sibling

    Hiển thị các thẻ sibling sau thẻ đóng của thẻ div class=”input-wrapper focused”.

    ca6db17bf580a6fc4d1afbb2ffc7759414d04c8c

    Vd9: parent

    Chọn tất cả thẻ cha của thẻ div class=”input-wrapper focused”.

    16d6dcb79d278326f232eee009cfbe09f898f5d1

    Vd10: preceding

    Chọn tất cả các thẻ trước thẻ input id=”next”, ngoại trừ các ancestor và attribute.

    b328b438090e7a67f184b8973b6b954530b9d395

    Vd11: contains()

    Chọn tất cả các phần tử có chứa text là “Create”.

    db1ce283d67472980df94cf6a90833ebe736b22a

    IV. Demo

    Ngữ cảnh: Đăng nhập vào trang http://www.chatwork.com/ thành công.

    Các bước thực hiện:

    Bước 1. Tạo một Project mới.

    • i. Nhấp chọn File –> New Profile.
    • ii. Nhập Name và chọn Type – Directory, Format – HTML.
    211ec6ddfbb1457c6d91357e968da51ff864779c

    Bước 2. Tạo Interface.

    • i. Nhấp phải chọn Project folder và chọn Test Suite.
    a75a1823bf1388333107559ca6b994df0fa5eda6

    ii. Đặt tên “Interface”.

    0530a02b087c4672f7252509de509e2a8c87ddf5

    iii. Nhấp phải chọn thư mục Interface –> New Resource.

    562c945bba4f5e3da63be958a413d6f2940b929a
    bcfa6c3d571a0987751ec8afd4936b0e3f1020bb

    iv. Nhấp phải chọn Interface resource –> New Scalar.

    d988bc7f6a51bed3d96e8202bfb69cf5bba18661
    337f06616c72772a4fcdf662e1e8e9a625f9b61f

    Bước 3. Tạo New Action.

    • i. Nhấp phải chọn Project Name –> New Suite.
    • ii. Tạo mới suite “Action”.
    273b7ee418507ff200fa1c86a1ba1862e256cf38

    iii. Tương tự như Interface, tạo New Source cho Action bằng cách nhấp phải chọn “Action”.

    e2718e1f236521b4a4b718515505604f222ce433
    1d1be3475039d40ae42c3f63572545128b1331ae

    iv. Thêm New User Keyword.

    c8d46e495b8f5950bae913cf471aea9828d1b6a5
    b0d36811a6a32695ea6475acf74008c3b6b67089

    v. Thêm Selenium2Library cho mỗi action.

    a7693ebb2f54ea76a63d8d8a0c28150b2a47c07b

    vi. Import Interface của page tương ứng vào bằng cách bấm Resource và trỏ đến thư mục chứa Interface.

    2382ecdba71ea77a1292701f1e9fe93a7256247a

    vii. Tạo các bước thực thi kiểm thử:

    dccb5b180a148c84e80df86b0af263a09d9fd5c8

    Bước 4. Tạo Testsuite.

    • i. Nhấp phải chọn thư mục dự án, chọn “New Suite”. Tạo Test Suite bằng File txt.
    a10719a6e9e839600ff05d04ef1ea03215334f40
    e22a1270422cc84de7dc71f6394a32097e641ce8

    ii. Thêm Library và Resource.

    8b4e0e02719a6413c7e314dbcdeb60310da347ee

    Bước 5. Tạo Testcase.

    • i. Nhấp phải chọn testsuite vừa tạo, chọn “New Test Case”. Sau đó điền tên Test case.
    e6ef5e416f1ca33cef7966fd0b5427c22a330394
    284310247cf64ebe4c844d291836fc06eb897a5f
    • ii. Viết script cho testcase.
    0bdb909df2a546a4ac483f0de1554d7125f914db

    Bước 6. Thực thi testsuite.

    d57e08d43866db4c2b1f0e55a629f8a0d4363ff4

    Bước 7. Kiểm tra kết quả.

    e7f0ef58678c18a5b97a60c8acd846d5db588259
    f8da3fd9b4bf31c6e5d335ef0a9da96f099c8370
    9dbf74f16b6f43d9f5f288b6a647d3bb3ec9b686

    V. Tham khảo

    Techtalk via Viblo

  • APPIUM Tutorial cho kiểm thử Android & iOS Mobile App

    Appium là gì?

    APPIUM là framework mã nguồn mở để kiểm thử UI cho các ứng dụng di động. Appium cho phép thử nghiệm ứng dụng native, hibrid và ứng dụng web, hỗ trợ test automation trên các thiết bị cũng như cả emulator hoặc simulator. Nó còn hỗ trợ kiểm thử cross-platform , tức là API đơn hoạt động cho kiểm thử Android và iOS.

    APPIUM KHÔNG phụ thuộc vào hệ điều hành thiết bị di động. Bởi vì APPIUM có khung hoặc trình bao bọc dịch các lệnh Selenium WebSearch thành các lệnh UIAutomation (iOS) hoặc UIAutomator (Android) tùy thuộc vào loại thiết bị, hệ điều hành nào.

    Appium hỗ trợ tất cả các ngôn ngữ có Selenium client librarie như- Java, Objective-C, JavaScript với node.js, PHP, Ruby, Python, C #, v.v.

    Trong hướng dẫn này, chúng ta sẽ tìm hiểu về:

    • APPIUM hoạt động như thế nào?
    • Điều kiện tiên quyết để sử dụng APPIUM
    • Cài đặt máy tính để bàn Appium:
    • Appian Inspector
    • Đính kèm Trình giả lập Android với Appium
    • Trường hợp kiểm tra APPIUM cho ứng dụng Android gốc (Máy tính)
    • Hạn chế sử dụng APPI
    • Các lỗi thường gặp và các bước khắc phục sự cố trong Appium

    APPIUM hoạt động như thế nào?

    • Appium là một ‘HTTP Server’ được viết bằng nền tảng Node.js và điều khiển iOS và phiên Android sử dụng giao thức dây JSON của Webdo. Do đó, trước khi khởi chạy Máy chủ Appium, Node.js phải được cài đặt sẵn trên hệ thống.
    • Khi Appium được tải xuống và cài đặt, thì máy chủ sẽ được thiết lập trên máy của chúng tôi hiển thị API REST.
    • Nó nhận được kết nối và yêu cầu lệnh từ máy khách và thực hiện lệnh đó trên thiết bị di động (Android / iOS).
    • Nó phản hồi lại với các phản hồi HTTP. Một lần nữa, để thực hiện yêu cầu này, nó sử dụng các khung tự động kiểm tra di động để điều khiển giao diện người dùng của các ứng dụng. Một khung như: –
    • Apple Cụ cho iOS (Dụng cụ chỉ khả dụng trong Xcode 3.0 trở lên với OS X v10.5 trở lên)
    • Google UIAutomator cho Android API cấp 16 trở lên
    • Selendroid cho API Android cấp 15 trở xuống

    Điều kiện tiên quyết để sử dụng APPIUM

    1. Cài đặt ANDROID SDK (Studio)
    2. Cài đặt JDK (Bộ phát triển Java)
    3. Cài đặt Eclipse
    4. Cài đặt TestNg cho Eclipse
    5. Cài đặt máy chủ Selenium
    6. Appium Client Library
    7. APK App Info on Google Play
    8. js (Không bắt buộc – Bất cứ khi nào máy chủ Appium được cài đặt, theo mặc định, nó đi kèm với “Node.exe” & NPM. Nó được bao gồm trong phiên bản hiện tại của Appium.)
    9. Install Appium Desktop

    Cài đặt Appium Desktop: Appium Studio là một ứng dụng GUI nguồn mở để cài đặt Máy chủ Appium. Nó đi kèm với tất cả các điều kiện tiên quyết để cài đặt và sử dụng Máy chủ Appium. Nó cũng có Thanh tra để nhận thông tin cơ bản trên Ứng dụng của bạn. Nó đi kèm với một Trình ghi để tạo mã soạn sẵn để tự động hóa các ứng dụng di động của bạn.

    Bước 1)

    Truy cập http://appium.io/ và nhấp vào Tải xuống Appium.

    Bước 2)

    Đối với Windows, chọn tệp exe và tải xuống. Tệp khoảng 162 MB sẽ mất thời gian để tải xuống dựa trên tốc độ internet của bạn.

    Bước 3)

    Nhấp vào exe đã tải xuống.

    Bước 4)

    Trên máy Windows, không cần cài đặt Appium. Nó chạy trực tiếp từ exe. Khi bạn nhấp vào exe, bạn sẽ thấy hình ảnh sau đây trong vài phút.

    Đối với Mac, bạn cần cài đặt dmg

    Bước 5)

    Tiếp theo bạn sẽ thấy Cửa sổ khởi động máy chủ. Nó điền vào tùy chọn máy chủ và cổng mặc định mà bạn có thể thay đổi. Nó cũng đề cập đến phiên bản của Appium đang được sử dụng.

    Bước 6)

    Khi nhấp vào nút Máy chủ Bắt đầu, một máy chủ mới được khởi chạy trên máy chủ và cổng được chỉ định. Đầu ra nhật ký máy chủ được hiển thị.

    Bước 7)

    Nhấp vào Cửa sổ Phiên mới .

    Bước 8)

    Bạn có thể nhập Khả năng mong muốn và bắt đầu một phiên.

    Appian Inspector

    Tương tự như công cụ ghi và phát lại Selenium IDE, Appium có ‘Inspector’ để ghi và phát lại. Nó ghi lại và phát hành vi ứng dụng gốc bằng cách kiểm tra DOM và tạo các tập lệnh thử nghiệm bằng bất kỳ ngôn ngữ nào bạn muốn. Tuy nhiên, hiện tại, không có hỗ trợ cho Thanh tra Appium cho Microsoft Windows. Trong Windows, nó khởi chạy Appium Server nhưng không kiểm tra các phần tử. Tuy nhiên, trình xem UIAutomator có thể được sử dụng như một tùy chọn để Kiểm tra các phần tử.

    Các bước để bắt đầu với Appium Inspector trên máy Mac: –

    Bước 1)

    Tải xuống và khởi động máy chủ Appium của bạn với Địa chỉ IP mặc định 0.0.0.0 và cổng 4725.

    Chọn tệp nguồn hoặc tệp .app từ cục bộ để kiểm tra. Kiểm tra hộp kiểm ‘Đường dẫn ứng dụng’ để bật nút ‘Chọn’.

    Bước 2)

    Bây giờ, bấm vào nút ‘Chọn’ sẽ cung cấp tùy chọn để duyệt và chọn tệp kiểm tra từ ổ đĩa cục bộ.

    Bước 3)

    Khởi động Trình mô phỏng trên máy Mac.

    Bước 4)

    Nhấp vào nút ‘Khởi chạy’ từ góc trên bên phải, cho phép biểu tượng màu xanh lam. Một lần nữa, nhấp vào biểu tượng màu xanh này, nó sẽ mở Trình kiểm tra và Trình mô phỏng Appium với một ứng dụng được chọn trước.

    Bước 5)

    • Khởi chạy Trình kiểm tra Appium của bạn sẽ hiển thị phân cấp thành phần trong cấu trúc theo cột. Ngoài ra, người dùng có thể áp dụng các hành động bằng các nút như Tap, Swipe, v.v.

    Bước 6)

    Nhấp vào nút ‘Dừng’ để dừng ghi âm.

    Attach Android Emulator cho Appium

    Bước 1)

    Cài đặt SDK Android trong hệ thống của bạn.

    Chuyển đến Bảng điều khiển >> Hệ thống và Bảo mật >> Hệ thống và từ bảng điều khiển bên trái, nhấp vào Settings Cài đặt hệ thống nâng cao ‘. Từ ‘Thuộc tính hệ thống’ bật lên, nhấp vào tab ‘Nâng cao’ và sau đó nhấp vào nút “Biến môi trường”.

    Bước 2)

    Bây giờ, từ ‘Biến môi trường’ bật lên, ‘nhấp đúp chuột vào’ Đường dẫn ‘và đặt biến ANDROID_HOME trỏ đến thư mục SDK của bạn. Trong đường dẫn nối thêm toàn bộ đường dẫn thư mục SDK.

    ví dụ: C: \ Người dùng \ ABC \ Máy tính để bàn \ adt-bundled-windows-x86_64-20140321 \ sdk

    Bước 3)

    Khởi động trình giả lập Android hoặc bất kỳ thiết bị Android nào gắn vào hệ thống của bạn (Đảm bảo bạn đã bật tùy chọn Gỡ lỗi Android trong thiết bị Android của mình. Để kiểm tra Tùy chọn gỡ lỗi. Đi đến Cài đặt thiết bị >> Tùy chọn nhà phát triển >> Kiểm tra “Tùy chọn gỡ lỗi” ).

    Bước 4)

    Mở Dấu nhắc lệnh và điều hướng đến thư mục \ platform-tools \ của SDK Android (Ví dụ: D: \ adt-bundle-windows-x86_64-20130514 \ sdk \ platform-tools).

    Bước 5)

    • Chạy lệnh ‘adb thiết bị’. Bạn có thể thấy thiết bị được kết nối của mình được liệt kê trong cửa sổ Dấu nhắc Lệnh. (Trong CMD ghi ‘> thiết bị adb’- Lệnh này sẽ liệt kê các phiên bản trình giả lập được kết nối. Ví dụ: adb hès giả lập-5554 cài đặt <Vị trí của tệp .apk>)

    Bước 6)

    • Chạy lệnh ‘adb start-server’. Nó sẽ khởi động máy chủ ADB sẽ được Appium sử dụng để gửi lệnh đến thiết bị Android của bạn.

    Bước 7)

    Bây giờ, điều hướng đến thư mục Appium trong hệ thống của bạn và bắt đầu Appium bằng cách nhấp vào tệp Appium.exe.

    Bước 8)

    Không thay đổi địa chỉ IP hoặc số cổng và nhấp vào nút ‘Khởi chạy’. Bảng điều khiển Appium của bạn bắt đầu từ 127.0.0.1:4723 như hiển thị bên dưới.

    Bước 9)

    Nhấp vào nút ‘Bắt ​​đầu’, máy chủ Appium bắt đầu chạy trên hệ thống của bạn.

    Trường hợp kiểm tra APPIUM cho ứng dụng Android gốc (Máy tính)

    Bước 1)

    Tải xuống plugin nhật thực ADT hoặc tải xuống gói ADT riêng tại đây

    Bước 2)

    Mở Eclipse và tạo một dự án mới >> Gói >> Lớp

    Bước 3)

    Nhập thư viện Selenium và Testng bên trong dự án mới đó.

    Bước 4)

    Bây giờ Tạo Chương trình thử nghiệm nhỏ cho ‘Calculator.app’ để tổng hai số.

    gói src_Appium; 
    nhập java.net.MalformedURLException; 
    nhập java.net.URL; 
    nhập org.openqa.selenium.By; 
    nhập org.openqa.selenium.WebDriver; 
    nhập org.openqa.selenium.WebEuity; 
    // nhập org.openqa.selenium.remote.CapabilityType; 
    nhập org.openqa.selenium.remote.DesiredCapabilities; 
    nhập org.openqa.selenium.remote.RemoteWebDriver; 
    nhập org.testng.annotations. *; 
    
    
    Máy tính lớp công khai { 
    Trình điều khiển WebDriver; 
    
    @B BeforeClass 
    void void setUp () ném MalformedURLException { 
    	// Thiết lập các khả năng mong muốn và chuyển gói hoạt động ứng dụng và gói ứng dụng Android cho Appium 
    	DesiredCapabilities ability = new DesiredCapabilities ();
    	ability.setCapability ("BROWSER_NAME", "Android"); 
    	ability.setCapability ("VERSION", "4.4.2"); 
    	ability.setCapability ("deviceName", "Trình giả lập"); 
    	ability.setCapability ("platformName", "Android"); 
     
       
       ability.setCapability ("appPackage", "com.android.calculator2"); 
    // Tên gói này của ứng dụng của bạn (bạn có thể lấy nó từ ứng dụng thông tin apk) 
    	ability.setCapability ("appActivity", "com.android.calculator2.Calculator"); 
       trình điều khiển = new RemoteWebDriver (URL mới ("http://127.0.0.1:4723/wd / hub "), khả năng);
    
       // định vị Văn bản trên máy tính bằng cách sử dụng By.name () 
       WebEuity Two = driver.findEuity (By.name ("2")); 
       hai.click (); 
       WebEuity plus = driver.findEuity (By.name ("+")); 
       dấu cộng.click (); 
       WebEuity bốn = driver.findE bổ sung (By.name ("4")); 
       bốn.click (); 
       WebEuity bằngTo = driver.findE bổ sung (By.name ("=")); 
       bằngTo.click (); 
       // xác định vị trí hộp chỉnh sửa của máy tính bằng cách sử dụng By.tagName () 
       WebEuity results = driver.findEuity (By.tagName ("EditText")); 
    	// Kiểm tra giá trị tính toán trên hộp chỉnh sửa
    khẳng định kết quả.getText (). bằng ("6"): "Giá trị thực tế là:" + results.getText () + "không khớp với giá trị mong đợi: 6"; 
    
    } 
    
    @AfterClass 
    công khai void void () { 
    	// đóng trình điều khiển ứng 
    	dụng.quito (); 
    } 
    }
    

    Những mặt hạn chế của APPI

    Appium không hỗ trợ thử nghiệm Phiên bản Android thấp hơn 4.2 Hỗ trợ hạn chế để thử nghiệm ứng dụng lai. ví dụ: không thể kiểm tra hành động chuyển đổi của ứng dụng từ ứng dụng web sang nguồn gốc và ngược lại. Không hỗ trợ để chạy Appium Inspector trên Microsoft Windows.

    Nguồn: https://www.guru99.com/introduction-to-appium.html

  • Big endian và Little endian

    Big endian và Little endian

    Tiếp theo bài viết “Lan man về trình biên dịch (compiled) và trình thông dịch (interpreted)”, ở bài viết này ta sẽ làm rõ hơn cái cách mà máy tính lưu trữ dữ liệu.

    Tôi thấy bài viết “LITTLE ENDIAN VS. BIG ENDIAN” rất hay nên xin phép tác giả đưa lên đây để cùng nhau tìm hiểu .

    Little endian và big endian, đây là hai phương thức khác nhau để lưu trữ dữ liệu dạng nhị phân (binary). Bình thường thì chúng ta cũng chẳng cần quan tâm đến chúng làm gì. Bởi mọi việc sẽ được tự động hoá hết.

    Thế nhưng có những tình huống, ví dụ khi phải xử lý các tập tin có cấu trúc, tập tin binary, nhất là những tập tin được ghi bằng ngôn ngữ khác, thì việc hiểu về little endian và big endian là rất quan trọng. Bởi nếu không, rất có thể chúng ta sẽ đọc sai thứ tự và xử lý với dữ liệu được hiểu sai.

    Dữ liệu

    Dữ liệu là thể hiện của thông tin dưới dạng lưu trữ được. Thông tin là thứ trừu tượng, không có hình dạng, đó là những hiểu biết về các sự vật, sự việc xung quanh chúng ta. Để lưu trữ, cũng như truyền đạt thông tin đến mọi người, chúng ta cần đến dữ liệu. Dữ liệu có thể là chữ viết, hình ảnh được ghi trên giấy, tất cả chúng ta dữ liệu mà con người có thể hiểu được.

    Nhưng những dữ liệu đó cần phải được mã hoá một lần nữa, nếu chúng ta muốn lưu trữ chúng trên máy tính. Như chúng ta đều biết, máy tính chỉ làm việc với dữ liệu được mã hoá dưới dạng nhị phân, vậy nên mọi dữ liệu cần được mã hoá thành nhị phân mới có thể xử lý trên máy tính được.

    Thực ra, máy tính không hiểu được các ký tự 0, 1 trong hệ nhị phân đâu, nó hoạt động theo các tín hiệu điện tử. Mô tả chính xác thì rất khó, nhưng chúng ta có thể hiểu “sơ sơ” rằng, gặp bit 1 thì sẽ có dòng diện, gặp bit 0 thì không có. Như vậy, các bit 0, 1 được xử lý thành các tín hiệu điện tử tương ứng, và chúng ta coi đó như máy tính đã hiểu được dữ liệu nhị phân.

    Thế nhưng, mặc dù cùng sử dụng tín hiệu dạng nhị phân, các máy tính khác nhau cũng không thực sự nói chung một ngôn ngữ. Cũng giống như coi người vậy, khi nhìn các ký tự abc có người hiểu, có người không. Máy tính khi nhìn vào các tín hiệu tương ứng với các ký hiệu 0 hay 1, mỗi máy tính có thể hiểu theo một cách khác nhau.

    Thế nhưng, rất may là các máy tính vẫn hoạt động theo những tiêu chuẩn chung, thế nên nó vẫn có thể giao tiếp với nhau được. Tuy nhiên, lưu ý rằng, không phải bất cứ lúc nào, các máy tính cũng có thể hiểu được lẫn nhau.

    Trong máy tính, các dữ liệu nhị phân không được xử lý theo từng bit riêng lẻ, mà được xử lý thành từng khối 8 bit một, và đơn vị xử lý nhỏ nhất này gọi là byte.

    Ví dụ, số nguyên 123456789 được biểu diễn dưới dạng nhị phân sẽ là (ở đây tôi cho rằng kiểu dữ liệu int sẽ có kích thước là 4 byte, tuy nhiên, nhiều hệ thống 64 bit đã nâng kích thước này lên 8 byte)

    00000111 01011011 11001101 00010101
    

    Để ngắn gọn, chúng ta có thể viết nó dưới dạng hexa như sau:

    07 5b cd 15
    

    Đã có bao giờ, bạn tự hỏi, khi ghi dữ liệu này trên đĩa cứng chẳng hạn, nó được ghi thế nào chưa. Bạn cho rằng, nó sẽ được ghi lần lượt theo thứ tự mà chúng ta đang đọc và viết ở trên, thì bạn đã nhầm.

    Đây là cách viết theo kiểu số Ả rập cho chúng ta dễ hiểu thôi, máy tính không “đọc” các ký tự giống như chúng ta nên nó cũng không lưu trữ giống cách chúng ta viết các ký tự này ra đâu. Việc ghi dữ liệu như thế nào chính là lúc little endian và big endian được dùng đến.

    Đây là cách viết theo kiểu số Ả rập cho chúng ta dễ hiểu thôi, máy tính không “đọc” các ký tự giống như chúng ta nên nó cũng không lưu trữ giống cách chúng ta viết các ký tự này ra đâu. Việc ghi dữ liệu như thế nào chính là lúc little endian và big endian được dùng đến.

    Little endian và big endian là gì?

    Little endian và big endian là hai phương thức khác nhau để lưu trữ dữ liệu. Sự khác biệt của little endian và big endian khi lưu trữ chính là ở việc sắp xếp thứ tự các byte dữ liệu.

    Trong cơ chế lưu trữ little endian (xuất phát từ “little-end” nghĩa kết thúc nhỏ hơn), byte cuối cùng trong biểu diễn nhị phân trên sẽ được ghi trước. Ví dụ 123456789 ghi theo kiểu little endian sẽ thành

    15 cd 5b 07
    

    Hơi ngược một chút đúng không? Big endian (xuất phát từ “big-end”) thì ngược lại, là cơ chế ghi dữ liệu theo thứ tự bình thường mà chúng ta vẫn dùng. 123456789 được lưu trữ vẫn theo đúng thứ tự là

    07 5b cd 15
    

    Các thuật ngữ big-end hay little-end xuất phát từ cuốn tiểu thuyết Gulliver du ký (Gulliver’s Travels), trong đó nhân vật Lilliputans tranh luận về việc nên đập trứng bằng đầu to hay nhỏ.

    Và ngành IT đã ứng dụng thuật ngữ ngày, tương đối giống với nghĩa gốc. Lưu ý rằng, little endian hay big endian chỉ khác nhau ở cách sắp xếp các byte dữ liệu, còn thứ tự từng bit trong byte thì giống nhau. Rất may, các máy tính vẫn có điểm trung này.

    Thêm một lưu ý nữa rằng, little endian hay big endian chỉ khác biệt khi cần lưu trữ những dữ liệu có nhiều byte. Những dữ liệu chỉ có 1 byte (ví dụ ký tự ASCII) thì không ảnh hưởng gì (chính xác là dù dùng phương thức nào kết quả cũng như nhau).

    Little endian và big endian được dùng trên những máy tính nào?

    Việc sắp xếp các byte dữ liệu theo kiểu little endian hay big endian không chỉ xảy ra khi chúng ta lưu trữ dữ liệu ra bộ nhớ ngoài. Mọi hoạt động của máy tính đều sử dụng dữ liệu nhị phân, nên little endian/big endian hiện hữu trong mọi hoạt động của máy tính.

    Ngoài việc sử dụng little endian/big endian một phần phụ thuộc vào phần mềm (do lập trình viên cố ý sử dụng một trong hai loại, hoặc ngôn ngữ lập trình quy định trước), nó còn phụ thuộc vào bộ vi xử lý của chính máy tính đó.

    Các bộ vi xử lý Intel đều sử dụng little endian, các bộ vi xử lý cả ARM trước đây cũng là little endian, nhưng hiện này ARM đã nâng cấp vi xử lý của mình thành bi-endian (tức là xử lý cả little endian và big endian).

    Các bộ vi xử lý PowerPC và SPARK trước đây đều là big endian, nhưng hiện nay chúng cũng được nâng cấp thành bi-endian.

    Các làm nào thì tốt hơn: little endian hay big endian?

    Little endian hay big endian cũng như tranh luận gốc về việc đập trứng, không có một phương thức nào thực sự tốt hơn phương thức nào.

    Little endian hay big endian chỉ khác nhau ở việc lưu trữ thứ tự các byte dữ liệu. Cả hai phương thức đều không làm ảnh hưởng đến tốc độ xử lý của CPU. Thế nên cả hai phương thức đều vẫn tồn tại song song và sẽ không bao giờ có thể có một câu trả lời thoả đáng: Phương thức nào thì tốt hơn?

    Mỗi phương thức đều có những lợi thế nhất định. Với little endian, vì byte nhỏ nhất luôn nằm bên trái, nó sẽ cho phép chúng ta đọc dữ liệu với độ dài tuỳ ý. Nó sẽ rất thích hợp nếu chúng ta cần ép kiểu, ví dụ từ int thành long int.

    Với giả định int là 4 byte, long int là 8 byte, nếu dùng little endian, khi ép kiểu, địa chỉ bộ nhớ không cần phải thay đổi, chúng ta chỉ cần ghi tiếp các byte lớn hơn mà thôi.

    Nhưng nếu cũng trường hợp đó, mà sử dụng big endian, thì chúng ta sẽ phải dịch địa chỉ bộ nhớ hiện tại thêm 4 byte nữa mới có không gian để lưu trữ.

    Nhưng big endian cũng có nhưng lợi thế nhất định, với việc đọc dữ liệu byte lớn nhất trước, nó sẽ rất dễ dàng kiểm tra một số là âm hay dương, do byte chứa dấu được đọc đầu tiên.

    Xem các byte dữ liệu trong bộ nhớ

    Chương trình C đơn giản nhau cho chúng ta cách nhìn về việc sắp xếp các byte trong bộ nhớ.

    #include <stdio.h>
    
    /* function to show bytes in memory, from location start to start+n */
    void
    show_mem_rep (char *start, int n)
    {
      int i;
      for (i = 0; i < n; i++)
        printf (" %.2x", start[i]);
      printf ("\n");
    }
    
    /* Main function to call above function for 0x01234567 */
    int
    main ()
    {
      int i = 0x01234567;
      show_mem_rep ((char *) &i, sizeof (i));
      return 0;
    }
    

    Khi thực thi chương trình trên, nếu máy của bạn là little endian thì kết quả sẽ là

     67 45 23 01
    

    còn nếu máy bạn là big endian thì nó sẽ hiển thị theo thứ tự thông thường

     01 23 45 67
    

    Có cách nào để xác định máy tính của chúng ta là little endian hay big endian hay không? Có vô số các cách khác nhau, dưới đây là một trong số những cách đó:

    #include <stdio.h>
    
    int
    main ()
    {
      unsigned int i = 1;
      char *c = (char *) &i;
      if (*c)
        printf ("Little endian");
      else
        printf ("Big endian");
      return 0;
    }
    

    Với đoạn code đơn giản trên, c là con trỏ, nó trỏ đến vùng nhớ của biến i là một số nguyên. Bởi vì số nguyên là kiểu dữ liệu nhiều byte, trong khí dữ liệu của char chỉ là một byte mà thôi, nên *c sẽ trả về giá trị là byte đầu tiên của số nguyên i.

    Nếu máy tính của chúng ta là little endian thì byte đầu tiên này sẽ là 1, ngược lại thì nó sẽ là 0.

    Điều này ảnh hưởng thế nào đến việc lập trình

    Về cơ bản thì little endian hay big endian không có ảnh hưởng lắm đến việc lập trình. Phần lớn các lập trình viên không cần quan tâm nhiều lắm, bởi mọi việc đã được các trình biên dịch/thông dich đảm nhiệm hết.

    Tuy nhiên, một số trường hợp, chúng ta cần quan tâm, đặc biệt khi chuyển đổi dữ\ liệu giữa các máy tính khác nhau. Ví dụ: khi chúng ta cần xử lý một file có cấu trúc thế này, 4 byte đầu tiên là một số nguyên n, sau đó là n số nguyên, mỗi số chiếm 4 byte bộ nhớ, v.v…

    Trong trường hợp này, khi nhận file được tạo ra từ một máy tính khác, việc nó được ghi theo kiểu little endian hay big endian rõ ràng là ảnh hưởng rất nghiêm trọng, nếu sử dụng sai phương thức, chúng ta sẽ thu về dữ liệu sai.

    Một trường hợp khác nữa có thể xảy ra vấn đề là khi chúng ta ép kiểu cho các biến

    #include <stdio.h>
    
    int
    main ()
    {
      unsigned char arr[2] = { 0x01, 0x00 };
      unsigned short int x = *(unsigned short int *) arr;
      printf ("%d", x);
    
      return 0;
    }
    

    Trong đoạn code trên, chúng ta đã ép kiểu một array hai phần tử char thành một số nguyên 2 byte (short int). Trong ví dụ này, little endian hay big endian cũng có ảnh hưởng rất lớn.

    Một máy tính dùng little endian sẽ có kết quả là 1 trong khi big endian sẽ cho kết quả là 256. Để tránh những lỗi đáng tiếc có thể xảy ra, những code như trên cần phải tránh.

    NUXI là một vấn đề rất nổi tiếng liên quan đến little endian và big endian: UNIX được lưu trong một hệ thống big-endian sẽ được hiểu là NUXI trong một hệ thống little endian.

    Giả sử chúng ta cần lưu trữ 4 byte (UNIX) bằng hai số nguyên dạng short intUN và IX.

    #include <stdio.h>
    
    int
    main ()
    {
      short int *s; // pointer to set shorts
      s = (short int *)malloc(sizeof(short int));    // point to location 0
      *s = "UN";  // store first short: U * 256 + N (fictional code)
      s += 2;    // point to next location
      *s = "IX";  // store second short: I * 256 + X
    
      return 0;
    }
    

    Đoạn code trên hoàn toàn độc lập với hệ thống, bất kể nó là little hay big endian. Nếu chúng ta lưu trữ các giá trị “UN” và “IX” khi đọc ra, nó vẫn sẽ là “UNIX” hay không? Nếu mọi việc chỉ xảy ra trên một máy tính, dù là big endian hay little endian thì nó sẽ luôn là như vậy, bởi mọi thứ sẽ được tự động hoá giúp chúng ta.

    Với bất cứ dữ liệu nào cũng vậy, chúng ta luôn thu được dữ liệu đúng nếu đọc và ghi trong cùng một hệ thống. Thế nhưng, hãy xem xét kỹ hơn về việc sắp xếp các byte trong bộ nhớ.

    Một hệ thống big endian sẽ lưu trữ như sau:

    U N I X
    

    Còn một hệ thống little endian thì sẽ như sau:

    N U X I
    

    Mặc dù trông hơi ngược nhưng hệ thống little endian sẽ xử lý việc đọc giúp chúng ta, nên lưu trữ như vậy nhưng khi lấy ra chúng ta vẫn có dữ liệu ban đầu. Thế nhưng khi chúng ta ghi dữ liệu này ra file, chuyển sang một máy tính khác. Và mỗi máy tính lại xử lý theo cách riêng của nó thì UNIX trên máy big endian sẽ được hiểu là NUXI trên máy little endian (và ngược lại).

    Đây chính là vấn đều nguy hiểm nhất khi chúng ta trao đỏi dữ liệu qua lại giữa các máy tính với nhau, đặc biệt trong thời đại Internet ngày nay.

    Trao đổi dữ liệu giữa các máy có endian khác nhau

    Ngày nay, mọi máy tính đều được kết nối để trao đổi dữ liệu với nhau. Little endian hay big endian cũng đều phải trao đổi với nhau, nhưng làm thế nào để có hiểu được nhau khi chúng không nói chung một thứ tiếng?

    Có 2 giải pháp chính cho việc này

    Sử dụng chung định dạng

    Một phương án đơn giản nhất tất cả sử dụng chung một định dang khi truyền dữ liệu.

    Ví dụ những tập tin dạng PNG đều bắt buộc phải sử dụng big endian. Tương tự với các tập tin có cấu trúc khác. Đó là lý do vì sao chúng ta nhiều khi cần phải dùng những phần mềm chuyên dụng để đọc và ghi các file này.

    Thế nhưng trong kết nối với Internet, việc truyền dữ liệu còn phức tạp hơn thế. Chúng ta không thể cứ dùng một định dạng file nào đó, rồi truyền từng byte một sang máy khác được. Muốn tăng tốc độ, bắt buộc chúng ta phải truyền nhiều byte một lúc.

    Và khi đó chúng ta cần có một chuẩn chung. Hiện nay, chuẩn chung cho việc truyền dữ liệu trên mạng, gọi là network byte order chính là big endian. Thế nhưng, dù đã chuẩn chung rồi, thỉnh thoảng vẫn có những giao thức chơi chội hơn, sử dụng little endian.

    Để có thể chuyển đổi dữ liệu thành dữ liệu chuẩn theo network byte order, chương trình cần gọi hàm hton* (host-to-network) (trong ngôn ngữ C). Trong hệ thống big endian, hàm này không cần làm gì cả, còn little endian sẽ thực hiện chuyển đối các byte một chút.

    Dù hệ thống big endian không cần chuyển đổi dữ liệu, việc gọi hàm này vẫn là rất cần thiết. Chương trình của chúng ta có thể được viết bằng một ngôn ngữ (C) nhưng có thể được dịch và thực thi ở nhiều hệ thống khác nhau, việc gọi hàm này sẽ giúp chúng ta làm điều đó.

    Tương tự, ở chiều ngược lại, chúng ta cần gọi hàm ntoh* để chuyển đổi dữ liệu nhận được từ mạng về dữ liệu máy tính có thể hiểu được. Ngoài ra, chúng ta còn phải hiểu rõ kiểu dữ liệu mà chúng ta cần chuyển đổi nữa, danh sách các hàm chuyển đổi như sau:

    • htons – “Host to Network Short”
    • htonl– “Host to Network Long”
    • ntohs – “Network to Host Short”
    • ntohl – “Network to Host Long”

    Những hàm này vô cùng quan trọng khi thực hiện chia sẽ dữ liệu ở tầng thấp, ví dụ khi kiểm tra checksum của các gói tin chẳng hạn. Nếu không hiểu rõ về little endian và big endian thì khi cần làm việc về mạng, bạn sẽ gặp nhiều khó khăn.

    Sử dụng BOM (Byte Order Mark)

    Một phương án khác để giải quyết sự khác biệt về endian là sử dụng BOM (Byte Order Mark). Đây là một ký tự đặc biệt, có giá trị là 0xFEFF, được ghi ở vị trí đầu tiên của file.

    Nếu bạn đọc ký tự này là 0xFFFE (bị ngược) thì có nghĩa file này được ghi với endian khác với hệ thống của bạn, khi đó, bạn sẽ cần phải thay đổi phương thức đọc dữ liệu một chút.

    Có một vài vấn đề nhỏ với việc sử dụng BOM. Thứ nhất, BOM sẽ gây tăng dữ liệu được ghi vào file. Ngay cả khi chúng ta chỉ gửi đi 2 byte dữ liệu, chúng ta vẫn cần thêm 2 byte BOM nữa.

    Thứ hai, BOM không hoàn toàn thần thánh, bởi nó phụ thuộc vào lập trình viên. Có người có tâm thì đọc và xử lý khi gặp BOM, có người thì hoàn toàn bỏ quên nó và coi nói như dữ liệu thông thường. Unicode sử dụng BOM khi lưu trữ dữ liệu nhiều byte (nhiều ký tự Unicode được mã hoá thành 2, 3 thậm chí là 4 byte).

  • Lan man về trình biên dịch (compiled) và trình thông dịch (interpreted)

    Chắc hẳn các bạn đã rất quen thuộc với 2 cụm từ “trình biên dịch” và “trình thông dịch”.
    Mình xin phép tổng hợp lại và cùng nhau tìm hiểu kỹ hơn một chút nhé.

    Theo SGK Tin học lớp 11:

    Có một vài thứ ta cần bàn đến ở đây.
    Trước hết hãy làm rõ “biên dịch” và “thông dịch”.

    • Biên dịch là chuyển các tài liệu từ ngôn ngữ này sang ngôn ngữ khác, đảm bảo độ chính xác.
    • Thông dịch là chuyển nhanh các thông điệp từ ngôn ngữ này sang ngôn ngữ khác, đảm bảo độ chính xác.
      Người ta đưa thêm một từ “trình” vào trước để ám chỉ rằng, nó là khái niệm dành cho máy để phân biệt với “biên dịch viên” và “thông dịch viên” mà chúng ta vẫn hay dùng cho con người.

    Hãy tưởng tượng xem, trình biên dịch và trình thông dịch làm gì với một đoạn mã lệnh nhé.
    a. Trình biên dịch: lần lượt thực hiện các bước sau:
    B1: Duyệt, kiểm tra, phát hiện lỗi, xác địch chương trình nguồn có dịch được không.
    B2: Dịch chương trình nguồn thành chương trình đích để máy hiểu và lưu trữ lại.
    b. Trình thông dịch: lần lượt thực hiện các bước sau
    B1: Kiểm tra tính đúng đắn của câu lệnh
    B2: Chuyển đổi câu lệnh nguồn thành câu lệnh tương ứng trong ngôn ngữ máy
    B3: Thực hiện các câu lệnh vừa chuyển đổi

    Chung lại, trình biên dịch sẽ chuyển đổi toàn bộ mã nguồn sang mã máy, rồi chứa kết quả vào ổ đĩa cứng để có thể thực thi ở lần chạy sau. Chương trình ngôn ngữ cấp cao được chuyển đổi gọi là chương trình mã nguồn (source program) và chương trình ngôn ngữ máy tạo ra gọi là chương trình đối tượng (object program) hoặc mã đối tượng (objectcode).
    Cách hoạt động của trình thông dịch khác so với trình biên dịch. Thay vì chuyển đổi toàn bộ mã nguồn sang chương trình đối tượng thì cứ khi nào chạy thì trình thông dịch hoạt động chuyển sang mã máy và đưa ra kết quả ngay. Công việc này sẽ diễn ra tương tự ở lần chạy tiếp theo. (theo http://kienthucweb.net/nao-la-trinh-bien-dich-va-thong-dich.html)
    Một vài ngôn ngữ thông dịch phổ biến: PHP, Javascript, Perl, …
    Một vài ngôn ngữ biên dịch phổ biến: C, C++, Java, …
    Vậy thực sự cái gì làm công việc dịch này?
    Cụ thể, trong C là GCC, còn trong PHP là Zen Engine VM.
    Và đây là những gì diễn ra khi chúng ta chạy một đoạn mã PHP, tham khảo bài viết dưới đây:
    https://techmaster.vn/posts/34207/php-chay-nhu-nao-tu-source-code-den-render

    Có 2 thứ ta cần cùng nhau làm rõ hơn đó là, cái đích cuối cùng của cả 2 đều phải tạo ra một thứ ngôn ngữ mà máy phải hiểu và có thể chạy.
    Vậy có 2 vấn đề:

    1. Máy ở đây hiểu là gì?
    2. Thứ ngôn ngữ như thế nào thì máy hiểu?

    Vấn đề thứ nhất, máy chính là CPU (Central Processing Unit), nó chính là bộ xử lý trung tâm, là các mạch điện tử thực hiện các câu lệnh của chương trình bằng cách thực hiện các phép tính số học, logic, so sánh, các hoạt động nhập/xuất dữ liệu (I/O) cơ bản do mã lệnh chỉ ra. (tham khảo: https://vi.wikipedia.org/wiki/CPU)
    Vấn đề thứ 2, ngôn ngữ máy là ngôn ngữ nhị phân (biểu diễn dưới dạng các chuỗi bit 0, 1).

    Lại có câu hỏi khác được nêu ra, tại sao CPU hiểu được các bit 0, 1?
    OK, câu trả lời là: CPU hiểu được vì các bit 0 và 1 biểu diễn cho tín hiệu điện, 1 tương ứng với “bật”, 0 tương ứng với “tắt”.
    Mã máy chạy trong CPU là dãy các tín hiệu xung điện (0 và 1) mà trình biên dịch hoặc trình thông dịch đã tạo ra.

    Một CPU có tầm vài triệu con transistor (hoặc nhiều hơn), hầu hết vai trò của chúng là các cổng logic, làm nhiệm vụ đóng hoặc mở tùy vào tín hiệu xung điện (là 0 hoặc 1).

    Phù, lằng nhằng phết nhỉ. Đến đây mình xin phép dừng lại, vì có quá nhiều thứ phải đọc, phải tìm hiểu. Nhưng ít nhất mình cũng hình dung ra được cái cách mà máy tính nó làm việc khi ta chạy một đoạn mã lệnh.