2017-08-01T17:41:59Z
Using Headless Chrome with Selenium
While working on the second edition of my flask book, I was reviewing my Selenium tests, which allow me to automate a web browser and do end-to-end testing. In the current version of the book I recommend running these tests against Firefox. I thought this was a great opportunity to see how Headless Chrome works, as that eliminates the annoying browser window that pops out every time you run the tests.
The results are encouraging. This super short article describes what you need to do to set up Selenium to use the Headless Chrome browser.
Unit Test Structure
In case you haven't seen my book, the way I configure the tests that use Selenium within Python's unittest
framework is shown below:
from selenium import webdriver
class SeleniumTestCase(unittest.TestCase):
client = None
@classmethod
def setUpClass(cls):
# start Firefox
try:
cls.client = webdriver.Firefox()
except:
pass
# skip these tests if the browser could not be started
if cls.client:
# create the application
cls.app = create_app('testing')
cls.app_context = cls.app.app_context()
cls.app_context.push()
# create the database and populate with some fake data
db.create_all()
Role.insert_roles()
User.generate_fake(10)
Post.generate_fake(10)
# start the Flask server in a thread
threading.Thread(target=cls.app.run).start()
# give the server a second to ensure it is up
time.sleep(1)
@classmethod
def tearDownClass(cls):
if cls.client:
# stop the flask server and the browser
cls.client.get('http://localhost:5000/shutdown')
cls.client.close()
# destroy database
db.drop_all()
db.session.remove()
# remove application context
cls.app_context.pop()
def setUp(self):
if not self.client:
self.skipTest('Web browser not available')
def tearDown(self):
pass
# tests go here!
The setUpClass()
method creates a Selenium client, which is stored in the client
class variable, and also creates an application context and a database. Then a real Flask server is started in a background thread. You can't use the Flask test client for this type of test because the browser controlled by Selenium needs a real server it can connect to. The tearDownClass()
just destroys all the resources created in setUpClass()
. The setUp()
method checks that a client
instance exists, and if it doesn't, it tells the unit testing framework that the test needs to be skipped. This can happen if, for example, you did not have Firefox installed.
Replacing Firefox with Chrome Headless
The first thing you need to do to switch to Chrome is to install Chrome if you don't have it yet (obviously!), and then you need to install ChromeDriver, which is the little bit of glue that allows Selenium to send commands to Chrome and automate it. If you are on a Mac, then brew cask install chromedriver
is all you need to do. On other platforms, download an installer from the ChromeDriver site: https://sites.google.com/a/chromium.org/chromedriver/downloads.
To switch to Chrome, you just need to change the initialization of the client
class variable:
# start Chrome
try:
cls.client = webdriver.Chrome()
except:
pass
But this will still use a regular Chrome window. If you want to use the headless option, you have to add options:
# start Chrome
options = webdriver.ChromeOptions()
options.add_argument('headless')
try:
cls.client = webdriver.Chrome(chrome_options=options)
except:
pass
And that's it! Now you get your tests running in the same way as before, but on an invisible Chrome window that is not going to disrupt your other windows. If you want to switch back to the regular mode, just comment out the options.add_argument('headless')
line and you'll get a visible window that you can watch while the tests run.
So far my experience with Headless Chrome is positive. Have you tried it? Let me know how it works for you.
#1 Mark ten Brinke said 2017-08-13T10:41:54Z
#2 Miguel Grinberg said 2017-08-13T18:28:39Z
#3 Jose Luis Lopez Pino said 2017-08-20T22:33:42Z
#4 Miguel Grinberg said 2017-08-20T23:09:39Z
#5 Angel Talavera said 2017-08-22T16:56:14Z
#6 German Munoz said 2017-08-29T16:19:23Z
#7 Miguel Grinberg said 2017-08-29T16:50:44Z
#8 Dmitry said 2017-08-31T12:44:47Z
#9 Miguel Grinberg said 2017-09-01T19:55:52Z
#10 Leandro Garcia said 2017-09-24T17:20:05Z
#11 Miguel Grinberg said 2017-09-25T05:34:00Z
#12 metulburr said 2017-10-11T14:50:30Z
#13 Miguel Grinberg said 2017-10-11T21:54:52Z
#14 Lesha Pak said 2017-11-01T14:59:31Z
#15 Grey Li said 2017-11-25T03:42:19Z
#16 Kyle Lawlor said 2018-04-03T23:21:12Z
#17 Anurag Choudhary said 2019-11-21T07:12:11Z