Skip to content

Commit 2e15137

Browse files
committed
✅ test(pytest): add comprehensive pytest setup and tests for LangGraph agent
- Add .vscode/settings.json to configure pytest settings - Extend Makefile with granular test targets: unit, integration, e2e, all, watch modes - Add linting improvements and new development commands in Makefile for dev and dev_ui - Configure pytest asyncio options in pyproject.toml - Create pytest conftest.py fixture to load environment variables and skip tests if missing keys - Implement detailed end-to-end tests in tests/e2e_tests/test_react_agent_e2e.py to cover: - Simple question answering - Streaming responses with tool usage detection - Thread and conversation management - Concurrent request handling - API error handling and assistant listing - Streaming modes testing - Add integration test in tests/integration_tests/test_graph.py validating agent responses on LangChain founder - Ensure tests use asyncio and properly check expected outputs for correctness
1 parent fb411e8 commit 2e15137

File tree

7 files changed

+377
-18
lines changed

7 files changed

+377
-18
lines changed

.vscode/settings.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"python.testing.pytestArgs": [
3+
"tests"
4+
],
5+
"python.testing.unittestEnabled": false,
6+
"python.testing.pytestEnabled": true
7+
}

Makefile

Lines changed: 72 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,56 @@
1-
.PHONY: all format lint test tests test_watch integration_tests docker_tests help extended_tests
1+
.PHONY: all format lint test test_unit test_integration test_e2e test_all test_watch test_watch_unit test_watch_integration test_watch_e2e test_profile extended_tests dev dev_ui
22

33
# Default target executed when no arguments are given to make.
44
all: help
55

6-
# Define a variable for the test file path.
7-
TEST_FILE ?= tests/unit_tests/
6+
######################
7+
# TESTING
8+
######################
89

10+
# Legacy test command (defaults to unit tests for backward compatibility)
911
test:
10-
python -m pytest $(TEST_FILE)
12+
python -m pytest tests/unit_tests/
13+
14+
# Specific test targets
15+
test_unit:
16+
python -m pytest tests/unit_tests/
17+
18+
test_integration:
19+
python -m pytest tests/integration_tests/
20+
21+
test_e2e:
22+
python -m pytest tests/e2e_tests/
1123

12-
test_watch:
24+
test_all:
25+
python -m pytest tests/
26+
27+
# Watch mode for tests
28+
test_watch: test_watch_unit
29+
30+
test_watch_unit:
1331
python -m ptw --snapshot-update --now . -- -vv tests/unit_tests
1432

33+
test_watch_integration:
34+
python -m ptw --snapshot-update --now . -- -vv tests/integration_tests
35+
36+
test_watch_e2e:
37+
python -m ptw --snapshot-update --now . -- -vv tests/e2e_tests
38+
1539
test_profile:
1640
python -m pytest -vv tests/unit_tests/ --profile-svg
1741

1842
extended_tests:
19-
python -m pytest --only-extended $(TEST_FILE)
43+
python -m pytest --only-extended tests/unit_tests/
44+
45+
######################
46+
# DEVELOPMENT
47+
######################
48+
49+
dev:
50+
uv run langgraph dev --no-browser
51+
52+
dev_ui:
53+
uv run langgraph dev
2054

2155

2256
######################
@@ -32,13 +66,25 @@ lint_package: PYTHON_FILES=src
3266
lint_tests: PYTHON_FILES=tests
3367
lint_tests: MYPY_CACHE=.mypy_cache_test
3468

35-
lint lint_diff lint_package lint_tests:
69+
lint:
70+
python -m ruff check .
71+
python -m ruff format src --diff
72+
python -m ruff check --select I src
73+
python -m mypy --strict src
74+
mkdir -p .mypy_cache && python -m mypy --strict src --cache-dir .mypy_cache
75+
76+
lint_diff lint_package:
3677
python -m ruff check .
3778
[ "$(PYTHON_FILES)" = "" ] || python -m ruff format $(PYTHON_FILES) --diff
3879
[ "$(PYTHON_FILES)" = "" ] || python -m ruff check --select I $(PYTHON_FILES)
3980
[ "$(PYTHON_FILES)" = "" ] || python -m mypy --strict $(PYTHON_FILES)
4081
[ "$(PYTHON_FILES)" = "" ] || mkdir -p $(MYPY_CACHE) && python -m mypy --strict $(PYTHON_FILES) --cache-dir $(MYPY_CACHE)
4182

83+
lint_tests:
84+
python -m ruff check tests --fix
85+
python -m ruff format tests
86+
# Skip mypy for tests to allow more flexible typing
87+
4288
format format_diff:
4389
ruff format $(PYTHON_FILES)
4490
ruff check --select I --fix $(PYTHON_FILES)
@@ -55,10 +101,24 @@ spell_fix:
55101

56102
help:
57103
@echo '----'
58-
@echo 'format - run code formatters'
59-
@echo 'lint - run linters'
60-
@echo 'test - run unit tests'
61-
@echo 'tests - run unit tests'
62-
@echo 'test TEST_FILE=<test_file> - run all tests in file'
104+
@echo 'DEVELOPMENT:'
105+
@echo 'dev - run langgraph dev without browser'
106+
@echo 'dev_ui - run langgraph dev with browser'
107+
@echo ''
108+
@echo 'TESTING:'
109+
@echo 'test - run unit tests (default)'
110+
@echo 'test_unit - run unit tests only'
111+
@echo 'test_integration - run integration tests only'
112+
@echo 'test_e2e - run e2e tests only'
113+
@echo 'test_all - run all tests (unit + integration + e2e)'
63114
@echo 'test_watch - run unit tests in watch mode'
115+
@echo 'test_watch_unit - run unit tests in watch mode'
116+
@echo 'test_watch_integration - run integration tests in watch mode'
117+
@echo 'test_watch_e2e - run e2e tests in watch mode'
118+
@echo ''
119+
@echo 'CODE QUALITY:'
120+
@echo 'format - run code formatters'
121+
@echo 'lint - run linters (ruff + mypy on src/)'
122+
@echo 'lint_tests - run linters on tests (ruff only, no mypy)'
123+
@echo 'lint_package - run linters on src/ only'
64124

pyproject.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,15 @@ lint.ignore = [
6060
[tool.ruff.lint.pydocstyle]
6161
convention = "google"
6262

63+
[tool.pytest.ini_options]
64+
asyncio_mode = "auto"
65+
asyncio_default_fixture_loop_scope = "function"
66+
6367
[dependency-groups]
6468
dev = [
6569
"langgraph-cli[inmem]>=0.1.71",
6670
"pytest>=8.3.5",
71+
"pytest-asyncio>=0.23.0",
72+
"langgraph-sdk>=0.1.0",
73+
"mypy>=1.17.1",
6774
]

tests/conftest.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"""Pytest configuration and shared fixtures."""
2+
3+
import os
4+
from pathlib import Path
5+
6+
import pytest
7+
from dotenv import load_dotenv
8+
9+
10+
@pytest.fixture(scope="session", autouse=True)
11+
def load_env():
12+
"""Load environment variables from .env file for all tests."""
13+
# Find the project root (where .env is located)
14+
project_root = Path(__file__).parent.parent
15+
env_file = project_root / ".env"
16+
17+
if env_file.exists():
18+
load_dotenv(env_file)
19+
20+
# Ensure required environment variables are available for tests
21+
# You can add fallback values or skip tests if keys are missing
22+
required_keys = ["OPENAI_API_KEY", "TAVILY_API_KEY"]
23+
missing_keys = [key for key in required_keys if not os.getenv(key)]
24+
25+
if missing_keys:
26+
pytest.skip(f"Missing required environment variables: {missing_keys}")

tests/e2e_tests/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""End-to-end tests for the LangGraph ReAct agent."""

0 commit comments

Comments
 (0)