2016-12-08 17 views
6

Tôi đang làm việc thông qua gói selenium Haskell WebDriver để thử nghiệm, here.Một haskell "withSubprocess" xây dựng xung quanh một khối làm

Tôi có ví dụ này:

import Test.WebDriver 

firefoxConfig :: WDConfig 
firefoxConfig = defaultConfig 

main :: IO() 
main = runSession firefoxConfig $ do      
    openPage "http://google.com"        
    searchInput <- findElem (ByCSS "input[type='text']") 
    sendKeys "Hello, World!" searchInput      
    submit searchInput          
    closeSession            

Các getting started section nói rõ rằng khách hàng selen đòi hỏi một máy chủ selen để giao tiếp với

java -jar selenium-server-standalone-*.jar 

Nếu không có nó chạy, bạn có được điều này:

ghci λ> main 
*** Exception: FailedConnectionException2 "127.0.0.1" 4444 False connect: does not exist (Connection refused) 

Tôi muốn bọc toàn bộ tập lệnh thử nghiệm của mình vào một hàm khởi tạo t anh ta selenium-server, ghi lại pid của nó và giết (pid) sau khi tôi chạy phiên. Đó là, trong suốt thời gian của chính hiện tại của tôi, tôi muốn gọi java selenium-server vào sự tồn tại, nhưng tôi muốn nó dừng lại ngay khi cuộc gọi kết thúc.

Trong python Tôi muốn làm điều này bằng một cái gì đó giống như việc xác định một __enter__() và một __exit__() với thứ nước mắt xuống khác, subprocess.Popen, ghi lại id, giết nó, sau đó gọi

with Browser() as b: 
    do_stuff 

Tôi có cảm giác các thực thể runSession là loại điều tôi sẽ cần phải nhân rộng để bọc khởi động và teardown như thế này theo nghĩa là nó lấy khối firefoxConfig $ do làm đối số và tôi cũng muốn làm điều đó.

Tuy nhiên, tôi không thể hoàn toàn hiểu được các loại từ thẩm vấn runSession, làm thế nào để làm cho loại điều:

ghci λ> :t runSession 
runSession 
    :: Test.WebDriver.Config.WebDriverConfig conf => 
    conf -> WD a -> IO a 

Tôi nghĩ rằng tôi muốn được tìm kiếm một số loại withMonad tôi có thể áp dụng đối với này được áp dụng cho do. Tôi nghĩ cú pháp sẽ là một số loại ...

import Test.WebDriver 
import System.Process 

firefoxConfig :: WDConfig 
firefoxConfig = defaultConfig 

withBrowser :: Monad a -> Monad a -- maybe this type? 
withBrowser = do 
    r <- createProcess (proc "java -jar selenium-server-standalone-*.jar" []) 
    -- other magic here? 

main :: IO() 
main = withBrowser $ runSession firefoxConfig $ do      
    openPage "http://google.com"        
    searchInput <- findElem (ByCSS "input[type='text']") 
    sendKeys "Hello, World!" searchInput      
    submit searchInput          
    closeSession            

Làm cách nào để đạt được điều này? Là monad phải không? Có một thành ngữ Haskell hay chiến lược nào cho điều này không?

+1

Một 'Monad' không phải là một loại. Ngoài ra, hãy giữ nó đơn giản. 'createProcess 'và' runSession' đều sử dụng 'IO', vì vậy hãy ở trong' IO'. 'withBrowser :: IO a -> IO a'. – Zeta

+0

Tại sao không sử dụng tập lệnh shell? – Ingo

Trả lời

1

Về cơ bản, bạn chỉ muốn bracket từ https://hackage.haskell.org/package/base-4.9.0.0/docs/Control-Exception.html#v:bracket.

Nó cho phép bạn chỉ định thiết lập và tách các hành động IO để chạy xung quanh hành động thứ ba. Nó tự động xử lý việc đưa đầu ra của hành động thiết lập vào cả hai hành động chính và teardown, làm hành động thiết lập của bạn chỉ cần cung cấp cho PID như là kết quả, do đó, hành động teardown sẽ được nói những gì PID để giết.

Cái gì như:

withBrowser browserAction 
    = bracket startSelenium killSelenium (const browserAction) 

(Trong trường hợp tôi đã giả định bạn không muốn hành động chính phải mất một cuộc tranh cãi cho pid, vì vậy tôi sử dụng const để bỏ qua nó)

+0

Đó có phải là giá_để tránh arg không? – Mittenchops

+1

@Mittenchops Ban đầu tôi nghĩ đó là những gì bạn muốn, nhưng lưu ý rằng hành động dọn dẹp cũng không có lý lẽ. Vì vậy, bạn sẽ không có một cách để vận chuyển PID từ hành động khởi động đến một dọn dẹp (không gắn nó trong một IORef hoặc một cái gì đó). – Ben

Các vấn đề liên quan