Ever wish you could write acceptance or integration tests for your Go-based web app without bringing in Capybara? Allow me to introduce:
Agouti lets you write acceptance tests using Ginkgo and Gomega. It provides a clean interface for controlling and making assertions against a browser-based web service. It supports Selenium WebDriver, PhantomJS, and ChromeDriver, allowing you to run your tests headlessly or in any major browser. It also supports Ginkgo’s parallelization capabilities, allowing you to run your integration tests in parallel for quicker execution.
Although Agouti is intended to be used with Ginkgo and Gomega, Agouti’s core package is a general-purpose WebDriver API for Go that does not depend on either of them. It is even possible to use Agouti and Gomega together without Ginkgo.
Check it out at agouti.org and on Github!
Want to get started right now? Go get Agouti, install your choice of WebDriver, and use Ginkgo to bootstrap your package:
$ go get github.com/sclevine/agouti $ brew install phantomjs $ cd path/to/yourpackage $ ginkgo bootstrap --agouti
Let’s generate a template for a simple user login test:
$ ginkgo generate --agouti user_login
Now open it up in your favorite editor:
package yourpackage_test
import (
. "path/to/yourpackage"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
. "github.com/sclevine/agouti/core"
)
var _ = Describe("UserLogin", func() {
var page Page
BeforeEach(func() {
var err error
page, err = agoutiDriver.Page()
Expect(err).NotTo(HaveOccurred())
})
AfterEach(func() {
page.Destroy()
})
})
And start writing some integration specs!
package yourpackage_test
import (
. "path/to/yourpackage"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
. "github.com/sclevine/agouti/core"
)
var _ = Describe("User Login", func() {
var page Page
BeforeEach(func() {
var err error
page, err = agoutiDriver.Page()
Expect(err).NotTo(HaveOccurred())
})
AfterEach(func() {
page.Destroy()
})
It("should manage user authentication", func() {
By("redirecting the user to the login form", func() {
Expect(page.Navigate("http://localhost:3000")).To(Succeed())
Expect(page).To(HaveURL("http://localhost:3000/login"))
})
By("allowing the user to fill out the login form", func() {
userField := page.FindByLabel("User")
Eventually(userField).Fill("bob")).Should(Succeed())
passwordField := page.FindByLabel("Password")
Expect(passwordField.Fill("secret")).To(Succeed())
Expect(page.FindByLabel("Remember Me").Check()).To(Succeed())
Expect(page.Find("#login_form").Submit()).To(Succeed())
})
By("directing the user to the dashboard", func() {
Eventually(page).Should(HaveTitle("Dashboard"))
})
By("allowing the user to view his or her profile", func() {
Expect(page.FindByLink("Profile Page").Click()).To(Succeed())
profile := page.Find("section.profile")
greeting := profile.Find(".greeting")
Eventually(greeting).Should(HaveText("Hello!"))
Expect(profile.Find("img#profile_pic")).To(BeVisible())
})
By("allowing the user to log out", func() {
Expect(page.Find("#logout").Click()).To(Succeed())
Expect(page).To(HavePopupText("Are you sure?"))
Expect(page.ConfirmPopup()).To(Succeed())
Eventually(page).Should(HaveTitle("Login"))
})
})
})
(If the Expect(...).To(Succeed())
assertions seem a bit too verbose, or you like Capybara’s DSL, we have a package for that.)
About the Author