Removal of `no-self-use` from pylint 2.14.0
A really minor change in pylint 2.14.0 might be breaking your workflow, learn how to fix it!
Learn the basics around writing tests using pytest and selenium!
Have you ever used a web app that used to work one way, then it was updated, and now it doesn't? Issues like these are very common for all software, and is something that is typically solved by writing unit and integration tests. Writing these tests usually involves importing your code's methods and functions into a testing framework, (like pytest
for example) executing them with mocked data, and then making sure that the expected result happened. This is already a time-consuming process that most developers don't enjoy doing—even for back-end software that is a bit easier to import functions from—Add in web apps, the complexity of JavaScript, and the weirdness of various browsers... Before you know it, your in a hot mess. This post goes over the very simple basics of one potential method for testing the web-apps you create or use.
The Python package pytest
is a simple and easy framework for writing tests in native Python. It's by far not the only testing framework out there, but it is definitely quite popular, and if you are aspiring to be a developer or work more closely with building software from a network automation standpoint, it's definitely a great tool to carry in your belt. Read up more on pytest
here if you aren't already familiar with it.
Selenium is a chemical elemen—Hold on, wrong Selenium. Selenium
is a "suite of tools for automating web browsers." TL;DR, if you aren't familiar with it, it's a pretty neat tool that lets you control your browser of choice entirely programmatically. Now, you may have heard of other tools like BeautifulSoup
(also called bs4
) that are really helpful for parsing through HTML and XML files. Using requests
to pull down an HTML file and then bs4
to parse it works really well when parsing simple static web pages, but bs4
does not implement a full blown web browser/client—What this means is that if you have a web-app that loads some JavaScript and that JavaScript client modifies the HTML DOM based off of API calls and other functions that it runs, you won't really get much value out of bs4
. This is where Selenium
steps in. Selenium
essentially provides "drivers" for interacting with the various popular web browsers. It can run in a semi-interactive mode, or even entirely headless! This tool will be the backbone of the test examples in this post, so read up more on Selenium
here if you want more information.
Selenium
has bindings for a few popular programming languages, but in our example, we'll just be using Python's bindings since we're using pytest
. Installation of these bindings is quite simple and can be installed from PyPi with pip3
. This gives us a nice way to drive a web browser using python.
On top of the Selenium
bindings for Python, there is a neat library called pytest-selenium
that makes it easy to integrate these two bits of software.
Okay, if you're still with me by now, you're probably invested in this, and hopefully, you are comfortable with the basics of pytest
and Selenium
. If not, this example might help, otherwise, check out their official docs on getting started, they're quite good (and is what I based this example on).
First off, I'm going to create a Python venv
, as it always makes life easier:
[I] ┬─[chris@chris-lt01:~/c/selenium_sandbox]─[09:15:23 AM]─[G:main]
╰─>$ python3 -m venv .venv
[I] ┬─[chris@chris-lt01:~/c/selenium_sandbox]─[09:15:32 AM]─[G:main]
╰─>$ source .venv/bin/activate.fish
[I] ┬─[chris@chris-lt01:~/c/selenium_sandbox]─[09:15:33 AM]─[V:.venv]─[G:main]
╰─>$ which python3
/home/chris/code/selenium_sandbox/.venv/bin/python3
Now that I have a nice fresh venv
to work from, let's install our pre-reqs for Python
:
[I] ┬─[chris@chris-lt01:~/c/selenium_sandbox]─[09:17:11 AM]─[V:.venv]─[G:main]
╰─>$ pip install selenium pytest pytest-selenium ipython
On top of the python bits needed, we'll also download a Chrome from here and put that somewhere on our $PATH. I'm going to do this with my venv
activated so that the driver is stored with my venv
. We'll also throw in a dash of chmod +x
to make sure that this can be executed.
[I] ┬─[chris@chris-lt01:~/c/selenium_sandbox]─[09:22:35 AM]─[V:.venv]─[G:main]
╰─>$ unzip ~/Downloads/chromedriver_linux64.zip
Archive: /home/chris/Downloads/chromedriver_linux64.zip
inflating: chromedriver
[I] ┬─[chris@chris-lt01:~/c/selenium_sandbox]─[09:22:38 AM]─[V:.venv]─[G:main]
╰─>$ mv chromedriver ~/.local/bin/
[I] ┬─[chris@chris-lt01:~/c/selenium_sandbox]─[09:22:56 AM]─[V:.venv]─[G:main]
╰─>$ chmod +x ~/.local/bin/chromedriver
[I] ┬─[chris@chris-lt01:~/c/selenium_sandbox]─[09:23:05 AM]─[V:.venv]─[G:main]
╰─>$
Now let's create a new file called test.py
and build our first stupidly simple test:
import pytest
@pytest.mark.nondestructive
def test_get_title(selenium):
selenium.get('https://slash64.tech')
title = selenium.title
assert title == "/64"
Easy enough, right? Now, let's pull it all together and run pytest
:
[I] ┬─[chris@chris-lt01:~/c/selenium_sandbox]─[09:29:04 AM]─[V:.venv]─[G:main]
╰─>$ pytest test.py --driver Chrome
========================================== test session starts ==========================================
platform linux -- Python 3.8.10, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
driver: Chrome
sensitiveurl: .*
rootdir: /home/chris/code/synth_test_sandbox
plugins: variables-1.9.0, selenium-3.0.0, base-url-1.4.2, metadata-2.0.1, html-3.1.1
collected 1 item
test.py . [100%]
=========================================== 1 passed in 1.64s ===========================================
[I] ┬─[chris@chris-lt01:~/c/selenium_sandbox]─[09:29:20 AM]─[V:.venv]─[G:main]
╰─>$
And just like that, we have written a test that asserts that the title HTML element of this blog is the string /64
. Pretty neat! Just to prove that everything's working as intended, let's change line 7 of our test to assert title == "Google"
:
[I] ┬─[chris@chris-lt01:~/c/selenium_sandbox]─[09:30:06 AM]─[V:.venv]─[G:main]
╰─>$ pytest test.py --driver Chrome
========================================== test session starts ==========================================
platform linux -- Python 3.8.10, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
driver: Chrome
sensitiveurl: .*
rootdir: /home/chris/code/synth_test_sandbox
plugins: variables-1.9.0, selenium-3.0.0, base-url-1.4.2, metadata-2.0.1, html-3.1.1
collected 1 item
test.py F [100%]
=============================================== FAILURES ================================================
____________________________________________ test_get_title _____________________________________________
selenium = <selenium.webdriver.chrome.webdriver.WebDriver (session="7202d2deca09eb2044873f27382fe457")>
@pytest.mark.nondestructive
def test_get_title(selenium):
selenium.get('https://slash64.tech')
title = selenium.title
> assert title == "Google"
E AssertionError: assert '/64' == 'Google'
E - Google
E + /64
test.py:7: AssertionError
-------------------------------------------- pytest-selenium --------------------------------------------
Driver log: /tmp/pytest-of-chris/pytest-9/test_get_title0/driver.log
URL: https://slash64.tech/
WARNING: Failed to gather log types: Message: unknown command: Cannot call non W3C standard command while in W3C mode
======================================== short test summary info ========================================
FAILED test.py::test_get_title - AssertionError: assert '/64' == 'Google'
=========================================== 1 failed in 3.63s ===========================================
Very cool! You can see in the pytest
output that my website returned the string /64
so when we were expecting it to show up as Google
the test failed.
I hope this gives you some ideas around what you can do with pytest
and Selenium
. I know that this isn't an incredibly practical example, but I hope that it is enough to tickle your brain a bit and get you thinking about how you could use this in your day-to-day work, and hit me up if you have any questions.