-
-
Notifications
You must be signed in to change notification settings - Fork 884
Provide pytest fixture for user test startup #5302
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Provide pytest fixture for user test startup #5302
Conversation
97ef08d to
8ad82e4
Compare
8ad82e4 to
a2350b6
Compare
|
@rodja thanks for the review and fixing things 👍 I was wondering if we should call the fixture |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very clever @Rollmops. Thanks for providing the code. I like the implementation but hesitated with the demo you provided.
First: it should be in the user fixture documentation and not in the main testing section.
Second: The current demo is not compelling. The user_startup fixture in pytest has only a super narrow, specific purpose, which makes it difficult to find obvious examples for when its useful to replace it. Here's why:
-
Overlap with
userfixture capabilities: Most customizations (environment variables, mocks, test instrumentation) can be accomplished by overriding theuserfixture directly using pytest's fixture inheritance pattern:@pytest.fixture async def user(user): # this overrides and uses NiceGUI's provided fixture os.environ['TEST_MODE'] = 'true' yield user
-
Existing
main_filemechanism: Themain_fileconfiguration (viapytest.inior@pytest.mark.nicegui_main_file) already handles most "different app startup" scenarios.
Especially if you consider that the (quite undocumented)main_fileconfiguration does not run a file if you set it to an empty string. That should also work for your use case. -
Limited relevance of
ui.run()parameters: Most parameters passed toui.run()(likereload,show_welcome_message, etc.) are irrelevant in simulated testing since there's no real server or browser involved.
Still, the most compelling reason to override user_startup I could come up with is to control the ui.run() parameters, particularly when you want to test your application without a storage_secret:
@pytest.fixture
def user_startup():
def startup():
ui.run() # No storage_secret - test graceful degradation
return startupBecause the default user_startup hardcodes storage_secret='simulated secret' you cannot prevent this by overriding user. I find this a legitimate (still a bit artificial) test scenario.
I'm therefore not sure if we should have the user_startup fixture at all.
Maybe you could try the approach with an empty main_file configuration for your use case before we proceed?
|
@rodja Let me fiddle around and try to find a way how our use case can be accomplished with a Thanks for your effort and I will report back :-) |
You can do it without a main file. See this mini-example: def custom_setup():
@ui.page('/')
def page():
ui.label('Hello from my custom startup :-)')
@pytest.mark.nicegui_main_file('')
async def test_custom_startup(user: User) -> None:
custom_setup()
await user.open('/')
await user.should_see('Hello from my custom startup :-)') |
That is not working in my case, since i pass a I keep trying :-) |
|
Ok, I played with the Hopefully, a simplified example should demonstrates the problem I am running into: Our app server provides a database connection that can be used by the pages to do stuff.
import ...
start_test_server(my_package_under_test)
I could prepare the test data here, that would work (although, we have different apps with different test data in this project, so it would be necessary to provide multiple The Problem is, I have no way to access the test database object in my test (I even tried working with global variables ...).
import pytest
from nicegui.testing import User
async def test_my_app_does_something_with_the_db(user: User):
await user.open("/my-app")
# here i need the database to assert data The database is just an example. In Maybe I am overseeing something, but currently we are not able to migrate to |
|
Ah I see. That looks really complicated. In my analysis I stated that |
|
Just to clarify our problem with the The motivation in me initial post did not make that clear, I know :-) I will improve the docs asap. Edit: I updated the motivation in the PR description. |
2093273 to
74a89c4
Compare
74a89c4 to
a261a45
Compare
|
@rodja I just moved the documentation to the user fixture section and added a more meaningful example. If you think this might confuse users too much, I can remove it. Furthermore, I renamed the fixture to |
a261a45 to
c0bbd26
Compare
|
@Rollmops why are you keeping to force-push to this branch. It makes review quite hard. |
|
Sorry, I just wanted to keep the branch in sync with the main branch. I will stop it. |
We (I think GitHub in large) prefer merge. You may be coming from GitLab where their UI promotes rebase and force-push as a default? Also, generally speaking, no need to keep the branch in sync unless something new off main is needed in your code / there is merge conflict. (probably should document this in CONTRIBUTING.md?) |
|
#5380 has now been merged for 3.4. |
Motivation
In some circumstances, it is not feasible to use a file (e.g.
main.py) in your project to startup the ui for testing (via themain_filepytest ini config).E.g. Imagine your production code uses a database that is created before calling
ui.runand passed to the pages via some injection mechanism. In your test you want to:main_fileapproach)By providing a
pytest.fixturethat is starting up the ui, the test database could be injected there and in your actual test by another ´pytest.fixture´.This is of course not limited to a database.
Implementation
Using
pytestfixtures to provide custom startup functions is a natural and backward-compatible way of setting up your test startups.By default, the fixture
user_startupreturns the function that implements the 'old' behaviour. By overriding this fixture, you can provide your own startup function.Progress