|
1 | 1 | # Getting Started #1: Understanding Playwright Python |
| 2 | + |
| 3 | +This guide outlines how Playwright works in Python, and how to start writing tests in the format this blueprint recommends. |
| 4 | + |
| 5 | +## Contents |
| 6 | + |
| 7 | +- [Getting Started #1: Understanding Playwright Python](#getting-started-1-understanding-playwright-python) |
| 8 | + - [Contents](#contents) |
| 9 | + - [The Basics](#the-basics) |
| 10 | + - [How Does pytest Work](#how-does-pytest-work) |
| 11 | + - [Executing Tests](#executing-tests) |
| 12 | + - [Using pytest Logic](#using-pytest-logic) |
| 13 | + - [Utilising Playwright `codegen`](#utilising-playwright-codegen) |
| 14 | + - [Utilising Playwright `show-trace`](#utilising-playwright-show-trace) |
| 15 | + - [Further Reading](#further-reading) |
| 16 | + - [Appendix](#appendix) |
| 17 | + - [Info: What Is Chromium](#info-what-is-chromium) |
| 18 | + |
| 19 | +## The Basics |
| 20 | + |
| 21 | +For Python, Playwright is treated as a plugin for a unit testing framework called [pytest](https://docs.pytest.org/en/stable/) and |
| 22 | +expands the functionality provided by pytest to allow for interaction with browsers (and allow us to write UI tests using the same logic) |
| 23 | +along with other testing utilities. Because of this, in this blueprint you will see references to pytest regularly, as it is the engine |
| 24 | +that drives the test execution. |
| 25 | + |
| 26 | +## How Does pytest Work |
| 27 | + |
| 28 | +In the case of this blueprint, pytest works by |
| 29 | +[scanning directories within the code base to discover tests](https://docs.pytest.org/en/stable/explanation/goodpractices.html#test-discovery), |
| 30 | +where by default it looks for any files in the format of `test_*.py` or `*_test.py`. Once the files have been discovered, it'll check for any |
| 31 | +functions in the file starting with `test_` and if found, will execute that function as a test and collect the result. |
| 32 | + |
| 33 | +pytest will spin up any utilities it needs to execute tests (including any we choose to define), which for this framework includes a number of |
| 34 | +Playwright-specific objects we would likely want to utilise, including: |
| 35 | + |
| 36 | +- `page`: The Playwright page object, which we use to interact with a browser page during tests. You'll likely use this object for every test. |
| 37 | +- `browser`: The Playwright browser object, which we use to create and manage the browser directly and create new pages if required. It's unlikely you'll need to include this unless you have a very specific browser test. |
| 38 | +- `playwright`: The Playwright object, which we can use to manage the Playwright instance during testing. It's extremely unlikely you'll need this when pytest is the test executor. |
| 39 | + |
| 40 | +For further reading on pytest, it's recommended to read the [full documentation](https://docs.pytest.org/en/stable/). |
| 41 | + |
| 42 | +## Executing Tests |
| 43 | + |
| 44 | +Because pytest is the engine in this blueprint, we use the pytest command to initiate any test execution. This can be done as simply by using |
| 45 | +the following command in the command line against this blueprint (once the initial setup has been completed): |
| 46 | + |
| 47 | + pytest |
| 48 | + |
| 49 | +When using this command, it will run any tests with the default configuration provided by pytest and Playwright (which in the case of UI testing, |
| 50 | +normally means that it'll run all tests against the [Chromium](#info-what-is-chromium) browser that was installed). |
| 51 | + |
| 52 | +If you want to execute tests with specific settings, such as a specific browser or to specify specific tests to run, these can be passed in on the |
| 53 | +command line after the pytest command or via the [pytest.ini](../../pytest.ini) file (using the `addopts` section). |
| 54 | + |
| 55 | +For further reading on the kinds of settings you can apply with pytest, take a look at our [Quick Reference Guide](./Quick_Reference_Guide.md). |
| 56 | + |
| 57 | +## Using pytest Logic |
| 58 | + |
| 59 | +When we use Playwright with pytest, a number of objects that we may want to interact with are automatically generated, but the most pertinent |
| 60 | +of these is the `page` object, which represents the browser page object we want to interact with. Because it's provided automatically when we |
| 61 | +start a test run, we do not need to do any specific configuration with the test other than add a reference to this page object in the function |
| 62 | +arguments for the test like so: |
| 63 | + |
| 64 | + # Doing an import like this for the page object isn't required, but is considered good practice |
| 65 | + from playwright.sync_api import Page |
| 66 | + |
| 67 | + # An example call showing how the page object is brought into a test, and how it can be used |
| 68 | + def test_example(page: Page) -> None: |
| 69 | + page.goto("https://github.com/nhs-england-tools/playwright-python-blueprint") |
| 70 | + |
| 71 | +As you can see from the example, the only setup for the `page` object here is in the function arguments, and we can use it as needed in the test. |
| 72 | +We can also use this logic to create utilities and resources that can be utilised by our tests, and easily pass them in to be used as needed. |
| 73 | + |
| 74 | +The `page` object is an example of a fixture, which are actions that can be run before or after any tests we want to execute (these are known as |
| 75 | +hooks in other test automation frameworks). A fixture is normally defined at the start of a set of tests, in a format like so: |
| 76 | + |
| 77 | + # Doing an import like this for the page object isn't required, but is considered good practice |
| 78 | + from playwright.sync_api import Page |
| 79 | + |
| 80 | + # An example fixture, which runs before the start of every test in this module |
| 81 | + @pytest.fixture(autouse=True) |
| 82 | + def go_to_page(page: Page) -> None: |
| 83 | + page.goto("https://github.com/nhs-england-tools/playwright-python-blueprint") |
| 84 | + |
| 85 | + # An example test which continues on from the fixture above |
| 86 | + def test_example(page: Page) -> None: |
| 87 | + page.get_by_placeholder("Go to file").fill("test_example.py") |
| 88 | + page.get_by_label("tests/test_example.").click() |
| 89 | + expect(page.locator("#file-name-id-wide")).to_contain_text("test_example.py") |
| 90 | + |
| 91 | +This allows for easy reuse of steps and reducing overall test maintenance going forward. Fixtures can play a powerful part in how you design |
| 92 | +your tests, including creating utilities available for global use, or just adding repeatable actions in a way that can overall reduce the |
| 93 | +maintenance effort of your tests going forward. |
| 94 | + |
| 95 | +Further reading on fixtures can be found in the [Playwright documentation](https://playwright.dev/python/docs/test-runners#fixtures). |
| 96 | + |
| 97 | +## Utilising Playwright `codegen` |
| 98 | + |
| 99 | +If you're new to Playwright, Python or automating tests generally, then |
| 100 | +[Playwright provides a code generation tool](https://playwright.dev/python/docs/codegen#recording-a-test) that allows you to manually navigate |
| 101 | +through a browser to generate the code for a test. You can access the `codegen` tool by using the following command: |
| 102 | + |
| 103 | + # Load a empty browser window |
| 104 | + playwright codegen |
| 105 | + |
| 106 | +This will bring up a browser window, with the Playwright code generator running alongside, like so: |
| 107 | + |
| 108 | +<!-- vale off --> |
| 109 | + |
| 110 | +<!-- vale on --> |
| 111 | + |
| 112 | +When using the `codegen` tool, it is recommended to do the following: |
| 113 | + |
| 114 | +- Pass in a starting URL where possible to set the window at your starting location (e.g. `playwright codegen https://github.com/nhs-england-tools/playwright-python-blueprint`) |
| 115 | +- When using the Playwright Inspector window, set the target value to Pytest as it'll automatically format any generated tests into the pytest format we recommend using in this blueprint |
| 116 | + |
| 117 | +The `codegen` tool is particularly powerful, as it also allows you to consider assertions on the page you are hoping to test. Currently, you can do the following |
| 118 | +basic assertions using the `codegen` tool: |
| 119 | + |
| 120 | +- Assert visibility of an element on the page |
| 121 | +- Assert specific text is present within an element on the page |
| 122 | +- Assert an element on the page has a specific value |
| 123 | + |
| 124 | +These are accessible via the floating menu when using the `codegen` tool, as highlighted in green here: |
| 125 | + |
| 126 | +<!-- vale off --> |
| 127 | + |
| 128 | +<!-- vale on --> |
| 129 | + |
| 130 | +Whilst the `codegen` tool will provide you with the basic code to get started, it's recommended that once you've got a working test, you consider refactoring any |
| 131 | +code that has been provided and refine as needed. Having the ability to generate the code in this fashion allows you to create tests quickly and build up |
| 132 | +understanding of how to construct tests using Playwright Python, but you will soon discover that they may not be the most efficient in their raw state! |
| 133 | + |
| 134 | +## Utilising Playwright `show-trace` |
| 135 | + |
| 136 | +If you encounter issues when trying to execute your newly created tests, |
| 137 | +[Playwright also provides a trace functionality](https://playwright.dev/python/docs/trace-viewer-intro) that records each action a test |
| 138 | +undertakes and outlines where any issues or errors have occurred. This can be useful for a number of reasons, including: |
| 139 | + |
| 140 | +- Easily pinpointing problems within a test, including functional and performance concerns |
| 141 | +- Generating evidence to show stakeholders what a test actually does during execution |
| 142 | +- The ZIP file generated can be opened on any machine, or via a [browser utility provided by Playwright](https://trace.playwright.dev/) |
| 143 | + |
| 144 | +To open a trace file, use the following command (replacing `<path-to-file>` with the actual path to the trace.zip file generated): |
| 145 | + |
| 146 | + # Opens a trace file |
| 147 | + playwright show-trace <path-to-file> |
| 148 | + |
| 149 | +A trace file when opened looks like this: |
| 150 | + |
| 151 | +<!-- vale off --> |
| 152 | + |
| 153 | +<!-- vale on --> |
| 154 | + |
| 155 | +The primary information provided within the trace is: |
| 156 | + |
| 157 | +- A timeline of events with screenshots (at the top of the report) |
| 158 | +- A summary of each action undertaken (on the left side of the report) |
| 159 | +- A screenshot of the selected action (on the right side of the report) |
| 160 | +- The network and test activity (at the bottom of the report) |
| 161 | + |
| 162 | +As you can see, it provides a lot of information to work with to help demonstrate what a test is doing, and diagnosing |
| 163 | +any issues when something goes wrong. |
| 164 | + |
| 165 | +## Further Reading |
| 166 | + |
| 167 | +Here are some useful links for further reading beyond these guides: |
| 168 | + |
| 169 | +- [Playwright Python documentation](https://playwright.dev/python/docs/intro) |
| 170 | +- [pytest documentation](https://docs.pytest.org/en/stable/index.html) |
| 171 | + |
| 172 | +## Appendix |
| 173 | + |
| 174 | +### Info: What Is Chromium |
| 175 | + |
| 176 | +[Chromium](https://www.chromium.org/Home/) is one of the open source browser that comes bundled with Playwright on install (if using the instructions |
| 177 | +within the [README](../../README.md) of this blueprint) but also more importantly, serves as the base code for both Google Chrome and Microsoft Edge. |
| 178 | +Whilst this doesn't replace the need to test in independent browsers as required, Chromium provides the opportunity to do some initial broad testing |
| 179 | +which should largely be representative of the user experience with Chrome and Edge respectively. |
0 commit comments