Skip to content

Commit 8c3e637

Browse files
authored
Merge pull request #249 from posit-dev/modify-e2e-testing-docs
docs(testing): Update end-to-end-testing.qmd
2 parents a70d549 + efbef6a commit 8c3e637

File tree

1 file changed

+117
-58
lines changed

1 file changed

+117
-58
lines changed

docs/end-to-end-testing.qmd

Lines changed: 117 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,137 +1,196 @@
11
---
2-
title: End-to-end testing
2+
title: End-to-End Testing Your App
33
editor:
44
markdown:
55
wrap: sentence
66
---
77

8-
End-to-end testing is like checking your app from start to finish, just as a user would.
8+
### What is End-to-End Testing (and Why Should You Care)?
99

10-
Imagine you're using your Shiny app. You click buttons, enter data, and see results on a graph or a dashboard. End-to-end tests mimic these actions.
11-
Instead of manually clicking around, we write code to do this for us.
12-
The code interacts with your app like a user, checking if everything works as expected.
10+
Imagine you've built a beautiful, interactive Shiny app. You want to make sure everything works exactly as expected, every time, for every user. That's where end-to-end testing comes in.
1311

14-
#### Benefits
15-
- End-to-end tests find issues early, like broken links or unexpected behavior.
16-
- As your app grows, it becomes harder to keep track of all parts. Tests help ensure nothing breaks.
12+
**What it is:**
1713

14+
* End-to-end testing checks your *entire* Shiny app, from start to finish, as if a real person were using it.
15+
* It simulates user actions like clicking buttons, filling in forms, and navigating between different parts of your app.
16+
* It verifies that the app's outputs (like graphs, tables, and text) are correct based on those actions.
1817

19-
### Playwright
18+
**Why it's awesome:**
2019

21-
***Playwright*** is an open-source library developed by Microsoft. It enables developers to automate browser interactions and perform end-to-end testing of web applications.
20+
* **Early bug detection:** Find problems *before* your users do! No more embarrassing surprises.
21+
* **Confidence in changes:** When you update your app, tests make sure you haven't accidentally broken anything.
22+
* **Time saver:** Instead of manually clicking through your app every time you make a change, tests automate the process.
23+
* **Peace of mind:** Know that your app is working reliably, so you can focus on building new features.
2224

23-
Benefits of using Playwright for Shiny App testing
25+
### Introducing Playwright: A Comprehensive Automated Testing Solution for Web Applications
2426

25-
- **End-to-End Testing**: Playwright allows you to simulate real user interactions with your Shiny app, ensuring that the reactive components and user flows work as expected.
26-
- **Cross-Browser Testing**: Playwright supports multiple browsers like Chromium, Firefox, and Safari(Webkit), enabling you to test your Shiny app's compatibility across different browser environments.
27-
- **Dynamic wait times** Playwright provides dynamic wait times, automatically waiting for elements to be ready before interacting with them, which eliminates the need for explicit waits and reduces flakiness caused by timing issues.
27+
***Playwright*** is a robust, open-source automation framework developed by Microsoft that enables programmatic control of web browsers. This tool provides developers with the capability to automate interactions with web applications across Chrome, Firefox, and Safari, simulating user behavior in a controlled, reproducible environment.
2828

29-
For detailed information and guidance, check out the [official Playwright documentation](https://playwright.dev/python/).
29+
**Why Playwright is perfect for Shiny:**
3030

31-
### How it works: a basic example
31+
* **Handles interactivity:** It can interact with all those cool Shiny widgets like sliders, dropdowns, and buttons.
32+
* **Cross-browser testing:** Make sure your app works flawlessly on different browsers.
33+
* **Smart waiting:** Playwright automatically waits for your app to load and for elements to be ready, so your tests are reliable.
34+
* **Easy to learn:** The code is relatively straightforward, and we'll walk you through it.
3235

33-
Consider the following app that simply displays a message with double the slider value:
36+
Learn more at the [official Playwright documentation](https://playwright.dev/python/).
3437

35-
```{.python filename="app.py"}
38+
### Let's Build and Test a Simple Shiny App!
39+
40+
We'll start with a super simple example to show you the basics. Follow along, and you'll be writing your own tests in no time!
41+
42+
#### Step 1: Create Your First Shiny App
43+
44+
First, let's create a tiny Shiny app with just a slider and some text.
45+
46+
1. **Create a new file:** Create a file named `app.py`.
47+
2. **Copy and paste this code:**
48+
49+
```python
3650
from shiny import render, ui
3751
from shiny.express import input
3852

3953
ui.panel_title("Hello Shiny!")
4054
ui.input_slider("n", "N", 0, 100, 20)
4155

56+
4257
@render.text
4358
def txt():
4459
return f"n*2 is {input.n() * 2}"
60+
4561
```
4662

47-
If we want to test that the shiny app works for the following scenario:
63+
3. **What this app does:** This app displays a slider (labeled "N") that goes from 0 to 100. Below the slider, it shows the text "n*2 is \[value]", where \[value] is twice the current slider value.
64+
65+
#### Step 2: What Are We Testing?
66+
67+
Our goal is to write a test that does the following:
4868

49-
1. Wait for the Shiny app to finish loading
50-
1. Drag the slider to value as `55`
51-
1. Verify the output text changes to reflect the value of `n*2 is 110`
69+
1. **Opens the app:** Starts the Shiny app in a browser.
70+
2. **Moves the slider:** Sets the slider to a specific value (*55* in this case).
71+
3. **Checks the output:** Verifies that the text below the slider displays the correct result ("n*2 is 110").
5272

53-
The test code to test the shiny app to emulate the above scenario would be as following:
73+
#### Step 3: Write Your First Test!
5474

55-
```{.python filename="test_basic_app.py"}
75+
Now for the exciting part – writing the test code!
76+
77+
1. **Create a new file:** Create a new file named `test_basic_app.py` in the same directory as your `app.py` file. Remember, test file names must start with `test_`.
78+
2. **Copy and paste this code:**
79+
80+
```python
5681
from shiny.playwright import controller
5782
from shiny.run import ShinyAppProc
5883
from playwright.sync_api import Page
59-
from shiny.pytest import create_app_fixture
6084

61-
app = create_app_fixture("../app.py")
85+
def test_basic_app(page: Page, local_app: ShinyAppProc) -> None:
86+
# Navigate to the app URL when it's ready
87+
page.goto(local_app.url)
6288

63-
def test_basic_app(page: Page, app: ShinyAppProc):
64-
page.goto(app.url)
89+
# Controller objects for interacting with specific Shiny components
6590
txt = controller.OutputText(page, "txt")
6691
slider = controller.InputSlider(page, "n")
92+
93+
# Move the slider to position 55
6794
slider.set("55")
95+
96+
# Verify that the output text shows "n*2 is 110"
97+
# (since 55 * 2 = 110)
6898
txt.expect_value("n*2 is 110")
6999
```
70100

71-
Here's a breakdown of what's happening in the test code:
72-
73-
1. The code begins by importing the `controller` module. This module provides classes and methods for controlling Shiny [components](https://shiny.posit.co/py/components/). With these classes, you can mimic user interactions (e.g., `.set()`) and verify state (e.g., `.expect_value()`). [See here](https://shiny.posit.co/py/api/testing/) for more information on the available classes and methods.
101+
- **Understand role of Fixtures**
102+
- **ShinyAppProc**: Manages a Shiny application subprocess, handling lifecycle (startup, shutdown) and providing access to output streams.
103+
- **page**: Playwright object representing the browser tab.
104+
- **local_app**: Running instance of the Shiny application.
74105

75-
2. Defines ***test_basic_app*** function with ***page*** and ***app*** parameters. *page* is an instance of the Page class from the Playwright library, which represents a single tab in a browser, and *app* is an instance of the `ShinyAppProc` class, which represents the Shiny app being tested.
106+
- **Understand role of Controllers**
76107

77-
3. Navigates to the app's URL.
108+
Controllers such as `OutputText` and `InputSlider` provide abstraction over Playwright's low-level interactions by:
78109

79-
4. Creates instances of `OutputText` and `InputSlider` for UI elements.
80-
81-
5. Sets the slider value to `55`.
82-
83-
6. Checks if the output text displays `n*2 is 110` as expected.
110+
- Automatically handling element waiting and state changes
111+
- Offering specialized interfaces for specific Shiny component types
112+
- Managing Shiny-specific behaviors without additional code
113+
- Providing consistent patterns for testing similar components
84114

85115
And visually, this is what happens when the test runs:
86116

117+
87118
![](assets/end-to-end-test-workflow.png)
88119

120+
#### Step 4: Run Your Test!
89121

90-
### Run the test
122+
Before you run the test, you need to install a couple of things:
91123

92-
To run end-to-end tests, you'll first want to make sure `pytest` and the `pytest-playwright` plugin are installed:
124+
1. **Install pytest and pytest-playwright**: Open your terminal (or command prompt) and type:
93125

94126
```bash
95127
pip install pytest pytest-playwright
96128
```
97129

98-
Now, if you have the `app.py` and `test_basic_app.py` files in the same directory, from that same directory, run the `pytest` command:
130+
2. **Navigate to your app's directory**: In the terminal, use the `cd` command to go to the folder where you saved `app.py` and `test_basic_app.py`.
131+
132+
3. **Run the test**: Type the following command and press Enter:
99133

100134
```bash
101135
pytest
136+
```
137+
138+
You should see output similar to this:
102139

140+
```text
103141
======== test session starts ========
104-
platform darwin -- Python 3.10.12, pytest-7.4.4, pluggy-1.4.0
105-
configfile: pytest.ini
106-
plugins: asyncio-0.21.0, timeout-2.1.0, Faker-20.1.0, cov-4.1.0, playwright-0.4.4, rerunfailures-11.1.2, xdist-3.3.1, base-url-2.1.0, hydra-core-1.3.2, anyio-3.7.0, syrupy-4.0.5, shiny-1.0.0
107-
asyncio: mode=strict
108-
12 workers [1 item]
142+
... (some details about your setup)
109143
.
144+
======== 1 passed in 3.05s ========
145+
```
110146

147+
What does this mean?
111148

112-
======== 1 passed in 3.05s ========
149+
- The `.` (dot) means your test passed!
150+
- If you see an `F`, it means the test failed. Double-check your code and make sure you followed all the steps.
151+
152+
#### Visualize Your Test (Optional)
153+
154+
If you want to see what Playwright is doing, you can run the test in "headed" mode. This will open a browser window and show you the interactions.
155+
156+
```bash
157+
pytest --headed
113158
```
114159

115-
Each test inside the file is shown by a single character in the output:
160+
You can also specify a particular browser:
116161

117-
- `.` for passing
118-
- `F` for failure.
162+
```bash
163+
pytest --browser firefox
164+
```
119165

120-
For more information on different options for running tests (like running tests in headful mode or in different browsers), check out the [Playwright documentation](https://playwright.dev/python/docs/test-runners).
166+
### Adding Tests to an Existing Shiny App
121167

122-
### Add tests an existing app
168+
If you already have a Shiny app, you can easily add tests:
123169

124-
If you already have a shiny app and want to add a test file to it, enter the following command in your terminal/console:
170+
1. Open your terminal: Navigate to your app's directory.
171+
1. Run the shiny add test command:
125172

126173
```bash
127174
shiny add test
128175
```
129176

130-
This command will ask you for a path to the app file as well as a name for the test file.
131-
Just make sure to follow `pytest` conventions for naming the test file (should start with `test_`).
132-
Note that the test file this command provides will need to be updated with the test logic you want to add.
177+
1. Answer the prompts: It will ask for the path to your app file (e.g., `app.py`) and a name for your test file (e.g., `test_myapp.py`). Remember, the test file name must start with `test_`.
178+
179+
1. Edit the generated test file: This command creates a basic test file. You'll need to modify it to add your specific test scenarios (like the slider example above).
180+
181+
### Troubleshooting Common Issues
182+
183+
- Test fails with an error about finding an element: Make sure the IDs you're using in your test code (e.g., "txt", "n") match the IDs in your Shiny app code. Inspect your app's HTML in the browser's developer tools if you're unsure.
184+
185+
- Test is flaky (sometimes passes, sometimes fails): This can happen if your app takes a while to load or if there are timing issues. Playwright has built-in waiting mechanisms, but you might need to add explicit waits in some cases. See the [Playwright documentation](https://playwright.dev/python/docs/events#waiting-for-event) on waiting.
186+
187+
### Keep Exploring!
188+
189+
You've taken your first steps into the world of Shiny app testing! Here are some resources to help you learn more:
133190

191+
- [Shiny testing API documentation](https://shiny.posit.co/py/api/testing/) - This is your go-to guide for all the available testing methods in Shiny.
192+
- [Playwright documentation](https://playwright.dev/python/) - Learn more about Playwright's powerful features.
193+
- [pytest documentation](https://docs.pytest.org/en/stable/)
134194

135-
### Learn more
136195

137-
For more information about the testing methods available to the user, read the reference documentation about shiny testing API [here](https://shiny.posit.co/py/api/testing/).
196+
Happy testing! You're now well-equipped to build more robust and reliable Shiny apps.

0 commit comments

Comments
 (0)