Testing: runner.invoke reuses 'app' object, causing cached properties to exist when they're not supposed to #1318
-
        First Check
 Commit to Help
 Example Codefrom typer.testing import CliRunner
from .main import app
runner = CliRunner()
def test_list_databases() -> None:
    # This command causes an internal 'databases' object to be cached.
    result = runner.invoke(app, ["databases", "list"]
    assert result.exit_code == 0
def test_create_database() -> None:
    # This command accesses some cached properties (using cached-property package).
    # Those objects aren't supposed to exist yet, but they were already created in previous tests.
    # This causes the command to use old data and crash.
    result = runner.invoke(app, ["databases", "create", "tests-db", "MariaDB"])
    assert result.exit_code == 0DescriptionCliRunner.invoke reuses 'app' causing different behavior than just running them outside tests. My code uses some cached properties that get cached in earlier tests. Operating SystemLinux Operating System DetailsNo response Typer Version0.4.0 Python Version3.7 Additional ContextNo response  | 
  
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 1 reply
-
| 
         happening for me as well, some options get cached and re-used in the following tests, making the tests fail  | 
  
Beta Was this translation helpful? Give feedback.
-
| 
         This is not a Typer's issue.. You can solve this by re-importing the app on every test: import helloworld.main
...
def test_list_databases():
    importlib.reload(helloworld.main)
    result = runner.invoke(helloworld.main.app, ...)
    ...Working code examplehelloworld/main.py from functools import cached_property
import typer
class DatabaseManager:
    @cached_property
    def databases(self) -> list[str]:
        print(">> Creating databases list")
        return ["default-db"]
    def create(self, name: str) -> None:
        print(f">> Creating database {name}")
        self.databases.append(name)
manager = DatabaseManager()
app = typer.Typer()
@app.command()
def list_databases():
    for db in manager.databases:
        typer.echo(db)
@app.command()
def create_database(name: str):
    manager.create(name)
    typer.echo(f"Database {name} created")helloworld/test.py import importlib
from typer.testing import CliRunner
import helloworld.main
runner = CliRunner()
def test_list_databases():
    importlib.reload(helloworld.main)
    result = runner.invoke(helloworld.main.app, ["list-databases"])
    assert result.exit_code == 0
    assert ">> Creating databases list" in result.stdout
def test_create_database():
    importlib.reload(helloworld.main)
    result = runner.invoke(helloworld.main.app, ["create-database", "test-db"])
    assert result.exit_code == 0
    assert ">> Creating databases list" in result.stdout | 
  
Beta Was this translation helpful? Give feedback.
This is not a Typer's issue..
You can solve this by re-importing the app on every test:
Working code example
helloworld/main.py