How do you write end-to-end tests for a Polymer (JS) based application (circa May 2015)?

I have created a Polymer based application. I would like to write some end-to-end tests (not unit tests, but user behavior integration tests). How to do it currently (May 2015)?

+3


source to share


1 answer


I've spent the past few days researching this issue. Despite the sheer number of pages on the Internet devoted to one related topic or another, nothing documents a solution to this problem. I managed to put together something that works. So this is it. Hope this is useful for anyone looking to write end-to-end tests for Polymer applications. I am confident the best solution will be achieved when Polymer technologies, web components, and shadow DOM technologies mature.

Some details: Linux, want to automate testing in a script (ideally headless), the application consists of many Polymer elements and is served and loaded with data from a Django server.

Failed to try with PhantomJS

I first tried using casperjs and phantomjs. phantomjs is headless and casperjs has very good navigation support, so I thought it would be a good combination to complete. Unfortunately phantomjs doesn't support HTML5 imports, and the webcomponents.js polyfill doesn't seem to work on phantomjs.

Selenium use

I ended up with a Selenium / ChromeDriver based solution using the selenium python client. I haven't tested this with the Firefox driver, so I don't know if it works. Here's what you need to do:

To make things easier, create a directory where you want to place the file:

mkdir selenium

      

Install google chrome. I did it via google website and downloaded the Linux version. Then download selenium server 2.45 and chromedriver 2.15 to selenium directory. For example.

$ ls selenium/
chromedriver  selenium-server-standalone-2.45.0.jar

      

Then install Python Selenium API

$ pip install selenium

      

Run a simple test:

$ cd selenium
$ cat > test.py
from selenium import webdriver
driver = webdriver.Chrome('./chromedriver')
driver.get("http://localhost:8000/myapp/")
driver.implicitly_wait(3)
print driver.title
content = driver.find_element_by_css_selector('myelement::shadow h3')
print content.text
driver.close()
$ xvfb-run python test.py

      



(xvfb is needed to make test.py run headless)

test.py outputs the content of the h3 element to a custom Polymer element called myelement. For example. if the DOM looks like

<myelement>
  <h3>Hello World</h3>
</myelement>

      

Then test.py prints "Hello World".

The h3 element appears in the shadow of the DOM myelement. Chrome dev tool renders the CSS selector for this h3 as "myelement # shadow-root h3". Using Selenium you can access this h3 using "myelement :: shadow h3" as your CSS selector.

Tests and test data

I organized my tests as Python unittest test cases and wrote a test driver script. The script driver creates a Django dev server in the child process and runs "python -m unittest" in the parent process. Each test case uses the Selenium Python API to connect to the Django dev server. In the setUp and tearDown methods of each test case, I insert the test data into the database using the Django model classes.

I am running the driver script under xvfb - "xvfb-run python driver.py" - to make it headless.

Working with Ajax and two-way bindings

My Polymer app uses ajax to load data and two way bindings to render HTML templates. Polymer also renders templates and updates the DOM asynchronously. In my test cases, I depend on Selenium conditional wait to detect when the data load completes and the DOM is re-rendered. Implicit waiting (not a good idea, anyway) didn't work for me for some reason; the implicit wait just returns immediately. I also updated my HTML templates to be more testable - adding DOM IDs and unique text or CSS selectors to differentiate between different stages of the application.

Warning

A button with only internal HTML gets messy using Selenium. If you have a button like this, use ActionChains to click on it:

chain = ActionChains()
chain.move_to_element(element)
chain.click()
chain.perform()

      

+3


source







All Articles