@@ -14,57 +14,154 @@ configuration files. It can also load variables from `.env` files.
1414pip install pytest-env
1515```
1616
17- ## Usage
17+ ## Quick start
1818
19- ### TOML configuration (native form)
19+ Add environment variables to your ` pyproject.toml ` :
2020
21- Define environment variables under ` [tool.pytest_env] ` in ` pyproject.toml ` , or ` [pytest_env] ` in ` pytest.toml ` /
22- ` .pytest.toml ` :
21+ ``` toml
22+ [tool .pytest_env ]
23+ DATABASE_URL = " postgresql://localhost/test_db"
24+ DEBUG = " true"
25+ ```
26+
27+ Run your tests. The environment variables are now available:
28+
29+ ``` python
30+ import os
31+
32+
33+ def test_database_connection ():
34+ assert os.environ[" DATABASE_URL" ] == " postgresql://localhost/test_db"
35+ assert os.environ[" DEBUG" ] == " true"
36+ ```
37+
38+ ## How-to guides
39+
40+ ### Set different environments for test suites
41+
42+ Create a subdirectory config to override parent settings:
43+
44+ ```
45+ project/
46+ ├── pyproject.toml # [tool.pytest_env] DB_HOST = "prod-db"
47+ └── tests_integration/
48+ ├── pytest.toml # [pytest_env] DB_HOST = "test-db"
49+ └── test_api.py
50+ ```
51+
52+ Running ` pytest tests_integration/ ` uses the subdirectory configuration.
53+
54+ ### Switch environments at runtime
55+
56+ Use the ` --envfile ` CLI option to override or extend your configuration:
57+
58+ ``` shell
59+ # Override all configured env files with a different one.
60+ pytest --envfile .env.local
61+
62+ # Add an additional env file to those already configured.
63+ pytest --envfile +.env.override
64+ ```
65+
66+ Override mode loads only the specified file. Extend mode (prefix with ` + ` ) loads configuration files first, then the CLI
67+ file. Variables in the CLI file take precedence.
68+
69+ ### Load variables from ` .env ` files
70+
71+ Specify ` .env ` files in your configuration:
2372
2473``` toml
25- # pyproject.toml
2674[tool .pytest_env ]
27- HOME = " ~/tmp"
28- RUN_ENV = 1
29- TRANSFORMED = { value = " {USER}/alpha" , transform = true }
30- SKIP_IF_SET = { value = " on" , skip_if_set = true }
31- DATABASE_URL = { unset = true }
75+ env_files = [" .env" , " .env.test" ]
76+ ```
77+
78+ Create your ` .env ` file:
79+
80+ ``` shell
81+ DATABASE_URL=postgres://localhost/mydb
82+ SECRET_KEY=' my-secret-key'
83+ DEBUG=" true"
84+ ```
85+
86+ Files are loaded before inline variables, so inline configuration takes precedence.
87+
88+ ### Expand variables using other environment variables
89+
90+ Reference existing environment variables in values:
91+
92+ ``` toml
93+ [tool .pytest_env ]
94+ RUN_PATH = { value = " /run/path/{USER}" , transform = true }
95+ ```
96+
97+ The ` {USER} ` placeholder expands to the current user's name.
98+
99+ ### Set conditional defaults
100+
101+ Only set a variable if it does not already exist:
102+
103+ ``` toml
104+ [tool .pytest_env ]
105+ HOME = { value = " ~/tmp" , skip_if_set = true }
32106```
33107
108+ This leaves ` HOME ` unchanged if already set, otherwise sets it to ` ~/tmp ` .
109+
110+ ### Remove variables from the environment
111+
112+ Unset a variable completely (different from setting to empty string):
113+
34114``` toml
35- # pytest.toml or .pytest.toml
36- [pytest_env ]
37- HOME = " ~/tmp"
38- RUN_ENV = 1
39- TRANSFORMED = { value = " {USER}/alpha" , transform = true }
40- SKIP_IF_SET = { value = " on" , skip_if_set = true }
115+ [tool .pytest_env ]
41116DATABASE_URL = { unset = true }
42117```
43118
44- Each key is the environment variable name. The value is either a plain value (cast to string) or an inline table with
45- the following keys:
119+ ## Reference
120+
121+ ### TOML configuration format
122+
123+ Define environment variables under ` [tool.pytest_env] ` in ` pyproject.toml ` , or ` [pytest_env] ` in ` pytest.toml ` or
124+ ` .pytest.toml ` :
125+
126+ ``` toml
127+ # pyproject.toml
128+ [tool .pytest_env ]
129+ SIMPLE_VAR = " value"
130+ NUMBER_VAR = 42
131+ EXPANDED = { value = " {HOME}/path" , transform = true }
132+ CONDITIONAL = { value = " default" , skip_if_set = true }
133+ REMOVED = { unset = true }
134+ ```
135+
136+ Each key is the environment variable name. Values can be:
46137
47- | Key | Type | Description |
48- | ------------- | ------ | --------------------------------------------------------------------------- |
49- | ` value ` | string | The value to set |
50- | ` transform ` | bool | Expand ` {VAR} ` references in the value using existing environment variables |
51- | ` skip_if_set ` | bool | Only set the variable if it is not already defined |
52- | ` unset ` | bool | Remove the variable from the environment (ignores ` value ` ) |
138+ - ** Plain values** : Cast to string and set directly.
139+ - ** Inline tables** : Objects with the following keys:
53140
54- ### INI configuration
141+ | Key | Type | Description |
142+ | ------------- | ------ | ---------------------------------------------------------------------------- |
143+ | ` value ` | string | The value to set. |
144+ | ` transform ` | bool | Expand ` {VAR} ` references in the value using existing environment variables. |
145+ | ` skip_if_set ` | bool | Only set the variable if it is not already defined. |
146+ | ` unset ` | bool | Remove the variable from the environment (ignores ` value ` ). |
55147
56- Define environment variables as a line-separated list of ` KEY=VALUE ` entries under the ` env ` key:
148+ ### INI configuration format
149+
150+ Define environment variables as line-separated ` KEY=VALUE ` entries:
57151
58152``` ini
59153# pytest.ini
60154[pytest]
61155env =
62156 HOME =~/tmp
63157 RUN_ENV =test
158+ D:CONDITIONAL =value
159+ R:RAW_VALUE ={USER}
160+ U:REMOVED_VAR
64161```
65162
66163``` toml
67- # pyproject.toml
164+ # pyproject.toml (INI-style)
68165[tool .pytest ]
69166env = [
70167 " HOME=~/tmp" ,
@@ -74,133 +171,125 @@ env = [
74171
75172Prefix flags modify behavior. Flags are case-insensitive and can be combined in any order (e.g., ` R:D:KEY=VALUE ` ):
76173
77- | Flag | Description |
78- | ---- | ------------------------------------------------------------------ |
79- | ` D: ` | Default — only set if the variable is not already defined |
80- | ` R: ` | Raw — skip ` {VAR} ` expansion (INI expands by default, unlike TOML) |
81- | ` U: ` | Unset — remove the variable from the environment entirely |
82-
83- ### Precedence
174+ | Flag | Description |
175+ | ---- | ------------------------------------------------------------------- |
176+ | ` D: ` | Default — only set if the variable is not already defined. |
177+ | ` R: ` | Raw — skip ` {VAR} ` expansion (INI expands by default, unlike TOML). |
178+ | ` U: ` | Unset — remove the variable from the environment entirely. |
84179
85- When multiple configuration sources are present, the native TOML form takes precedence over the INI form. Within the
86- TOML form, files are checked in this order: ` pytest.toml ` , ` .pytest.toml ` , ` pyproject.toml ` . The first file containing a
87- ` pytest_env ` section wins.
180+ ** Note** : In INI format, variable expansion is enabled by default. In TOML format, it requires ` transform = true ` .
88181
89- ### Configuration file discovery
182+ ### ` .env ` file format
90183
91- The plugin walks the directory tree starting from the directory containing the configuration file pytest resolved
92- (` inipath ` ). For each directory it checks ` pytest.toml ` , ` .pytest.toml ` , and ` pyproject.toml ` in order, stopping at the
93- first file with a ` pytest_env ` section. This means a subdirectory config takes precedence over a parent config:
94-
95- ```
96- project/
97- ├── pyproject.toml # [tool.pytest_env] DB_HOST = "prod-db"
98- └── tests_integration/
99- ├── pytest.toml # [pytest_env] DB_HOST = "test-db"
100- └── test_api.py
101- ```
102-
103- Running ` pytest tests_integration/ ` uses ` DB_HOST = "test-db" ` from the subdirectory.
104-
105- If no TOML file with a ` pytest_env ` section is found, the plugin falls back to the INI-style ` env ` key.
106-
107- ### Loading ` .env ` files
108-
109- Use ` env_files ` to load variables from ` .env ` files. Files are loaded before inline ` env ` entries, so inline config
110- takes precedence. Missing files are silently skipped. Paths are relative to the project root.
184+ Specify ` .env ` files using the ` env_files ` configuration option:
111185
112186``` toml
113- # pyproject.toml
114187[tool .pytest_env ]
115188env_files = [" .env" , " .env.test" ]
116- API_KEY = " override_value"
117- ```
118-
119- ``` toml
120- # pytest.toml or .pytest.toml
121- [pytest_env ]
122- env_files = [" .env" ]
123189```
124190
125191``` ini
126- # pytest.ini
127192[pytest]
128193env_files =
129194 .env
130195 .env.test
131196```
132197
133- Files are parsed by [ python-dotenv] ( https://github.com/theskumar/python-dotenv ) , supporting ` KEY=VALUE ` lines, ` # `
134- comments, ` export ` prefix, quoted values (with escape sequences in double quotes), and ` ${VAR:-default} ` expansion:
198+ Files are parsed by [ python-dotenv] ( https://github.com/theskumar/python-dotenv ) and support:
199+
200+ - ` KEY=VALUE ` lines
201+ - ` # ` comments
202+ - ` export ` prefix
203+ - Quoted values with escape sequences in double quotes
204+ - ` ${VAR:-default} ` expansion
205+
206+ Example ` .env ` file:
135207
136208``` shell
137- # .env
138209DATABASE_URL=postgres://localhost/mydb
139210export SECRET_KEY=' my-secret-key'
140211DEBUG=" true"
141212MESSAGE=" hello\nworld"
213+ API_KEY=${FALLBACK_KEY:- default_key}
142214```
143215
144- ### Examples
216+ Missing ` .env ` files are silently skipped. Paths are resolved relative to the project root.
145217
146- ** Expanding environment variables ** — reference existing variables using ` {VAR} ` syntax:
218+ ### CLI option: ` --envfile `
147219
148- ``` toml
149- [pytest_env ]
150- RUN_PATH = { value = " /run/path/{USER}" , transform = true }
151- ```
220+ Override or extend configuration-based ` env_files ` at runtime:
152221
153- ``` ini
154- [pytest]
155- env =
156- RUN_PATH =/run/path/{USER}
222+ ``` shell
223+ pytest --envfile PATH # Override mode
224+ pytest --envfile +PATH # Extend mode
157225```
158226
159- In TOML, expansion requires ` transform = true ` . In INI, expansion is the default; use the ` R: ` flag to disable it .
227+ ** Override mode ** ( ` --envfile PATH ` ): Loads only the specified file, ignoring all ` env_files ` from configuration .
160228
161- ** Keeping raw values** — prevent ` {VAR} ` expansion:
229+ ** Extend mode** (` --envfile +PATH ` ): Loads configuration files first in their normal order, then loads the CLI file.
230+ Variables from the CLI file override those from configuration files.
162231
163- ``` toml
164- [pytest_env ]
165- PATTERN = { value = " /run/path/{USER}" }
166- ```
232+ Unlike configuration-based ` env_files ` , CLI-specified files must exist. Missing files raise ` FileNotFoundError ` . Paths
233+ are resolved relative to the project root.
167234
168- ``` ini
169- [pytest]
170- env =
171- R:PATTERN =/run/path/{USER}
172- ```
235+ ## Explanation
173236
174- ** Conditional defaults ** — only set when not already defined:
237+ ### Configuration precedence
175238
176- ``` toml
177- [pytest_env ]
178- HOME = { value = " ~/tmp" , skip_if_set = true }
179- ```
239+ When multiple configuration sources define the same variable, the following precedence rules apply (highest to lowest):
180240
181- ``` ini
182- [pytest]
183- env =
184- D:HOME =~/tmp
185- ```
241+ 1 . Inline variables in configuration files (TOML or INI format)
242+ 1 . Variables from ` .env ` files loaded via ` env_files `
243+ 1 . Variables already present in the environment (unless ` skip_if_set = false ` or no ` D: ` flag)
186244
187- ** Unsetting variables ** — completely remove a variable from ` os.environ ` (not the same as setting to empty string):
245+ When using ` --envfile ` , CLI files take precedence over configuration-based ` env_files ` , but inline variables still win.
188246
189- ``` toml
190- [pytest_env ]
191- DATABASE_URL = { unset = true }
192- ```
247+ ### Configuration format precedence
193248
194- ``` ini
195- [pytest]
196- env =
197- U:DATABASE_URL
198- ```
249+ When multiple configuration formats are present:
199250
200- ** Combining flags** — flags can be combined in any order:
251+ 1 . TOML native format (` [pytest_env] ` or ` [tool.pytest_env] ` ) takes precedence over INI format.
252+ 1 . Among TOML files, the first file with a ` pytest_env ` section is used, checked in order: ` pytest.toml ` ,
253+ ` .pytest.toml ` , ` pyproject.toml ` .
254+ 1 . If no TOML file contains ` pytest_env ` , the plugin falls back to INI-style ` env ` configuration.
201255
202- ``` ini
203- [pytest]
204- env =
205- R:D:TEMPLATE =/path/{placeholder}
206- ```
256+ ### File discovery
257+
258+ The plugin walks up the directory tree starting from pytest's resolved configuration directory. For each directory, it
259+ checks ` pytest.toml ` , ` .pytest.toml ` , and ` pyproject.toml ` in order, stopping at the first file containing a
260+ ` pytest_env ` section.
261+
262+ This means subdirectory configurations take precedence over parent configurations, allowing you to have different
263+ settings for integration tests versus unit tests.
264+
265+ ### When to use TOML vs INI format
266+
267+ Use the ** TOML native format** (` [pytest_env] ` ) when:
268+
269+ - You need fine-grained control over expansion and conditional setting.
270+ - Your configuration is complex with multiple inline tables.
271+ - You prefer explicit ` transform = true ` for variable expansion.
272+
273+ Use the ** INI format** (` env ` key) when:
274+
275+ - You want simple ` KEY=VALUE ` pairs with minimal syntax.
276+ - You prefer expansion by default (add ` R: ` to disable).
277+ - You are migrating from an existing INI-based setup.
278+
279+ Both formats are fully supported and can coexist (TOML takes precedence if both are present).
280+
281+ ### When to use ` .env ` files vs inline configuration
282+
283+ Use ** ` .env ` files** when:
284+
285+ - You have many environment variables that would clutter your config file.
286+ - You want to share environment configuration with other tools (e.g., Docker, shell scripts).
287+ - You need different ` .env ` files for different environments (dev, staging, prod).
288+
289+ Use ** inline configuration** when:
290+
291+ - You have a small number of test-specific variables.
292+ - You want variables to be version-controlled alongside test configuration.
293+ - You need features like ` transform ` , ` skip_if_set ` , or ` unset ` that ` .env ` files do not support.
294+
295+ You can combine both approaches. Inline variables always take precedence over ` .env ` files.
0 commit comments