JavaScript không đồng bộ và không đồng bộ / đang chờ trong các thử nghiệm Selenium WebDriver


Bùi Quang Hiếu
1 năm trước
Hữu ích 8 Chia sẻ Viết bình luận 0
Đã xem 1704

Selenium là một thư viện tuyệt vời. Nó hỗ trợ tất cả các trình duyệt chính, có tất cả các tính năng mà chúng tôi có thể sẽ cần, và hiện là tiêu chuẩn không chính thức trong các thử nghiệm trình duyệt hiện nay, và đúng như vậy.

(Đối với những người không biết, kiểm tra trình duyệt là các thử nghiệm chạy trình duyệt, tự động hóa trình duyệt để tương tác với ứng dụng giao diện người dùng của bạn và kiểm tra theo cách đó.)

Selenium có các ràng buộc cho nhiều ngôn ngữ - Java, C #, Python, Ruby và các ngôn ngữ khác. Điều này thật tuyệt - bạn có thể sử dụng ngôn ngữ yêu thích của mình để viết bài kiểm tra của mình.

Và nếu bạn là nhà phát triển front-end, bạn sẽ chọn ngôn ngữ nào? Chà, ngôn ngữ mà bạn có lẽ thấy thoải mái nhất là JavaScript. Nhưng bản chất không đồng bộ của JavaScript đặt ra những thách thức đặc biệt cho Selenium. Trong bài đăng này, tôi sẽ thảo luận về những thách thức đó và cách JavaScript (và Selenium) vượt qua những thách thức này.

Nhưng trước tiên, tôi muốn thảo luận về tính không đồng bộ trong JavaScript nói chung.

Không đồng bộ trong JavaScript

JavaScript là một ngôn ngữ không đồng bộ. Tất cả I / O trong đó sẽ (hầu như) luôn không đồng bộ. Có nghĩa là sẽ cần phải gọi lại để thông báo cho chương trình khi thao tác I / O được thực hiện. Nhưng điều này không đúng trong hầu hết các ngôn ngữ khác. Trong Java, ví dụ, thực hiện I / O là một hoạt động đồng bộ. Một bản sao tập tin trông như thế này:

byte[] content = Files.readAllBytes(Paths.get("source.txt"));

Files.write(Paths.get("target.txt"), content);

Đầu tiên, chúng tôi đọc một tập tin, sau đó chúng tôi sao chép nó. Đơn giản, không? Nhưng trong JavaScript, nó không đơn giản như vậy:

fs.readFile('source.txt', (_, content) => {
    fs.writeFile('target.txt', content, () => {
        console.log('done!')
    })
})

Lưu ý làm thế nào không có mã sau fs.readFile. Mã Real thực sự xảy ra trong cuộc gọi lại được chuyển đến fs.readFile. Đây là lý do tại sao các cuộc gọi lại trong NodeJS ở khắp mọi nơi!

Tại sao? Bởi vì mã JavaScript chỉ có một luồng. Nếu bạn chặn luồng đó, không có mã song song nào khác có thể chạy. Điều này có nghĩa là bạn không bao giờ phải chặn luồng của mình bằng I / O, điều đó có nghĩa là bạn cần cung cấp một cuộc gọi lại sẽ được gọi khi hoạt động I / O đó kết thúc và có mã cho phần tiếp theo của chương trình.

Thật không may, điều này làm cho một số mã thực sự xấu xí và bị xáo trộn. Hãy tưởng tượng bạn cần phải lặp lại một tập hợp các tập tin và sao chép chúng. Với các cuộc gọi lại, bạn không thể sử dụng các vòng lặp cho điều đó - bạn thực sự cần một số đệ quy để thực hiện điều đó. Đúng, mã không đồng bộ không giống như mã đồng bộ với các cuộc gọi lại - đó là một cách viết mã khác.

Và nó không đẹp.

Để bù đắp cho sự xấu xí này, ES2015 đã thêm Promisecác đối tượng. Sử dụng lời hứa, người ta có thể viết mã gọi lại theo cách đẹp hơn nhiều:

readFilePromise('source.txt')
    .then(content => writeFilePromise('target.txt', content))
    .then(() => console.log('done'))

Cái này đẹp hơn nhiều nhưng vẫn hơi khó hiểu. Và nó vẫn có các cuộc gọi lại trong đó (các thentrình xử lý (). Bởi vì, cuối cùng, Node.js chỉ có một luồng và bạn không được phép chặn nó bằng I / O.

Điều gì xảy ra nếu có một cách để bảo JavaScript chờ đợi lời hứa đó và sau đó tiếp tục từ vị trí đó một khi lời hứa được giải quyết và có giá trị? Vâng, có đấy! Nó được gọi là async / await:

async function main() {
    const content = await readFilePromise('source.txt')

    await writeFilePromise('target.txt', content)

    console.log('done')
}
main()

Yay! async / await xử lý tất cả những thứ xấu xí và dịch mã trên thành mã sử dụng lời hứa. Chúng tôi không quan tâm - nó chỉ hoạt động. Nó sẽ không bao giờ chặn trên I / O. Và chúng ta có thể sử dụng các vòng lặp for, và trong khi các vòng lặp, ngắt, và tiếp tục, và thử / bắt, như thể chúng ta đang lập trình ở chế độ đồng bộ. Chỉ cần đừng quên những awaitchức năng đó trả lại lời hứa hoặc những điều đồng thời kỳ lạ bắt đầu xảy ra.

Mã Selenium WebDriver

Với tất cả những điều trên, hãy tưởng tượng sự ngạc nhiên của tôi khi tôi thấy
mã Selenium WebDriver này :

const {Builder, By, Key, until} = require('selenium-webdriver');

let driver = new Builder()
    .forBrowser('firefox')
    .build();

driver.get('http://www.google.com/ncr');
driver.findElement(By.name('q')).sendKeys('webdriver', Key.RETURN);
driver.wait(until.titleIs('webdriver - Google Search'), 1000);
driver.quit();

Đây là, trong đất JavaScript, mã không thể. Tại sao? Xem driver.get(…)dòng nào? Đó là mã mà I / O - nó giao tiếp qua mạng đến một quy trình khác. Node.js không có khả năng chạy mã này một cách đồng bộ.

Tuy nhiên, có một lời hứa, không đồng bộ / chờ đợi hoặc gọi lại trong trang web! Cái này hoạt động ra sao? Chà, hóa ra tất cả chỉ là một trò lừa đảo. Bạn có thể tìm thấy các chi tiết bẩn thỉu  ở đây . Nhưng điều đang thực sự xảy ra là khi bạn gọi driver.getnó không thực sự điều hướng đến URL được cung cấp, mà chỉ là lên lịch cho việc thực hiện sau này. Và khi mã gọi driver.findElement, thì nó không thực sự thực thi điều đó, mà là lên lịch cho nó sau, sau driver.get. Tất cả đều được lên lịch để chạy không đồng bộ và không thực sự thực thi đồng bộ.

Đây là chương trình phức tạp tuyệt vời và là một kỳ công công nghệ trong chính nó! Nhưng tại sao họ làm điều đó? Họ đã làm điều đó để làm cho các trình kiểm tra được sử dụng để lập trình đồng bộ Java / C # / Python, không bị lập trình bởi chương trình không đồng bộ. Vì vậy, họ đã hack nó để làm cho nó trông đồng bộ, nhưng thực thi không đồng bộ.

Nhưng tại sao tôi gọi nó là hack? Có vẻ tốt, không? Ồ không. Thật không may, sự trừu tượng này rò rỉ như địa ngục . Nếu bạn đang gỡ lỗi mã và tiếp cận driver.getdòng, bạn hy vọng nó sẽ thực thi trong trình duyệt, nhưng không được. Và nếu bạn muốn thực hiện nếu dựa trên giá trị của một phần tử, thì bạn không thể, bởi vì bạn không thực sự có giá trị. Nó sẽ nhận được giá trị sau này.

May mắn thay, họ đã để lại một cửa hậu cho phép chúng tôi làm việc theo cách JavaScript JavaScript. Tất cả các chức năng trong Selenium WebDriver đều trả về a Promise, do đó, phần trên có thể được viết như sau:

const {Builder, By, Key, until} = require('selenium-webdriver');

let driverPromise = new Builder()
    .forBrowser('firefox')
    .build();

driverPromise.then(driver => driver.get('http://www.google.com/ncr'))
    .then(() => driver.findElement(By.name('q')))
    .then(element => element.sendKeys('webdriver', Key.RETURN))
    .then(() => driver.wait(until.titleIs('webdriver - Google Search'), 1000))
    .then(() => driver.quit())

Điều này tốt hơn, vì bây giờ mã của chúng tôi đã kiểm soát khi mọi thứ xảy ra, nhưng đó vẫn là mã Promise - không phải là cách tốt nhất để viết. Nhưng ít nhất, nó không ngụy trang thành một hệ thống đồng bộ, nó chạy không đồng bộ và mọi thứ hoạt động như mong đợi.

May mắn thay, bây giờ chúng tôi có async / đang chờ để giúp chúng tôi. Hãy viết mã ở trên theo kiểu async / await:

const {Builder, By, Key, until} = require('selenium-webdriver');

async function main() {
    let driver = await new Builder()
        .forBrowser('firefox')
        .build();

    await driver.get('http://www.google.com/ncr')

    const element = await driver.findElement(By.name('q'))

    await element.sendKeys('webdriver', Key.RETURN)
    await driver.wait(until.titleIs('webdriver - Google Search'), 1000)
    await driver.quit()
}
main())

Đó là nó - mã Selenium WebDriver trông  giống như mã đồng bộ, nhưng sử dụng Promise bên dưới và không có mã bị hack bên dưới làm những điều điên rồ.

Tương lai

Dường như cộng đồng Selenium hiểu rằng trò lừa đảo là chế độ đồng bộ / không đồng bộ này chỉ là tạm thời.

Trình quản lý lời hứa có trong mô-đun này đang trong quá trình loại bỏ theo các lời hứa JavaScript gốc. Đây sẽ là một quá trình dài và sẽ không được hoàn thành cho đến khi có hai bản phát hành LTS Node chính (xấp xỉ Node v10.0) hỗ trợ các chức năng không đồng bộ.

Vì vậy, hãy bắt đầu làm quen với việc sử dụng async / await trong Selenium WebDriver của bạn để kiểm tra, đó sẽ là cách duy nhất trong thị trấn.

Và đó là một điều tốt.

Những gì bây giờ?

Bạn muốn tìm hiểu thêm về các bài kiểm tra trình duyệt? Bạn có thể tìm hiểu thêm trong các bài viết trước của tôi - ở đâyở đây .

Hữu ích 8 Chia sẻ Viết bình luận 0
Đã xem 1704