huggingface--transformers
e06fe8e8c6
Secret Leaks / trufflehog (push) Failing after 1s
Build documentation / build (push) Failing after 1s
Build documentation / build_other_lang (push) Failing after 0s
CodeQL Security Analysis / CodeQL Analysis (push) Failing after 0s
PR CI / pr-ci (push) Failing after 1s
Slow tests on important models (on Push - A10) / Get all modified files (push) Failing after 1s
Slow tests on important models (on Push - A10) / Model CI (push) Has been skipped
Self-hosted runner (benchmark) / Benchmark (aws-g5-4xlarge-cache) (push) Has been cancelled
New model PR merged notification / Notify new model (push) Has been cancelled
Update Transformers metadata / build_and_package (push) Has been cancelled
1272 ่ก
50 KiB
Markdown
1272 ่ก
50 KiB
Markdown
<!--Copyright 2020 The HuggingFace Team. All rights reserved.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
|
the License. You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
|
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
specific language governing permissions and limitations under the License.
|
|
|
|
โ ๏ธ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
|
rendered properly in your Markdown viewer.
|
|
|
|
-->
|
|
|
|
# ํ
์คํธ[[testing]]
|
|
|
|
|
|
๋จผ์ ๐ค Transformers ๋ชจ๋ธ์ด ์ด๋ป๊ฒ ํ
์คํธ๋๋์ง ์ดํด๋ณด๊ณ , ์๋ก์ด ํ
์คํธ๋ฅผ ์์ฑ ๋ฐ ๊ธฐ์กด ํ
์คํธ๋ฅผ ๊ฐ์ ํ๋ ๋ฐฉ๋ฒ์ ์์๋ด
์๋ค.
|
|
|
|
์ด ์ ์ฅ์์๋ 2๊ฐ์ ํ
์คํธ ์ค์ํธ๊ฐ ์์ต๋๋ค:
|
|
|
|
1. `tests` - ์ผ๋ฐ API์ ๋ํ ํ
์คํธ
|
|
2. `examples` - API์ ์ผ๋ถ๊ฐ ์๋ ๋ค์ํ ์์ฉ ํ๋ก๊ทธ๋จ์ ๋ํ ํ
์คํธ
|
|
|
|
## Transformers ํ
์คํธ ๋ฐฉ๋ฒ[[how-transformers-are-tested]]
|
|
|
|
1. PR์ด ์ ์ถ๋๋ฉด 9๊ฐ์ CircleCi ์์
์ผ๋ก ํ
์คํธ๊ฐ ์งํ๋ฉ๋๋ค. ํด๋น PR์ ๋ํด ์๋ก์ด ์ปค๋ฐ์ด ์์ฑ๋ ๋๋ง๋ค ํ
์คํธ๋ ๋ค์ ์งํ๋ฉ๋๋ค. ์ด ์์
๋ค์
|
|
์ด [config ํ์ผ](https://github.com/huggingface/transformers/tree/main/.circleci/config.yml)์ ์ ์๋์ด ์์ผ๋ฏ๋ก ํ์ํ๋ค๋ฉด
|
|
์ฌ์ฉ์์ ๋ก์ปฌ ํ๊ฒฝ์์ ๋์ผํ๊ฒ ์ฌํํด ๋ณผ ์ ์์ต๋๋ค.
|
|
|
|
์ด CI ์์
์ `@slow` ํ
์คํธ๋ฅผ ์คํํ์ง ์์ต๋๋ค.
|
|
|
|
2. [github actions](https://github.com/huggingface/transformers/actions)์ ์ํด ์คํ๋๋ ์์
์ 3๊ฐ์
๋๋ค:
|
|
|
|
- [torch hub integration](https://github.com/huggingface/transformers/tree/main/.github/workflows/github-torch-hub.yml):
|
|
torch hub integration์ด ์๋ํ๋์ง ํ์ธํฉ๋๋ค.
|
|
|
|
- [self-hosted (push)](https://github.com/huggingface/transformers/tree/main/.github/workflows/self-push.yml): `main` ๋ธ๋์น์์ ์ปค๋ฐ์ด ์
๋ฐ์ดํธ๋ ๊ฒฝ์ฐ์๋ง GPU๋ฅผ ์ด์ฉํ ๋น ๋ฅธ ํ
์คํธ๋ฅผ ์คํํฉ๋๋ค.
|
|
์ด๋ `src`, `tests`, `.github` ํด๋ ์ค ํ๋์ ์ฝ๋๊ฐ ์
๋ฐ์ดํธ๋ ๊ฒฝ์ฐ์๋ง ์คํ๋ฉ๋๋ค.
|
|
(model card, notebook, ๊ธฐํ ๋ฑ๋ฑ์ ์ถ๊ฐํ ๊ฒฝ์ฐ ์คํ๋์ง ์๋๋ก ํ๊ธฐ ์ํด์์
๋๋ค)
|
|
|
|
- [self-hosted runner](https://github.com/huggingface/transformers/tree/main/.github/workflows/self-scheduled.yml): `tests` ๋ฐ `examples`์์
|
|
GPU๋ฅผ ์ด์ฉํ ์ผ๋ฐ ํ
์คํธ, ๋๋ฆฐ ํ
์คํธ๋ฅผ ์คํํฉ๋๋ค.
|
|
|
|
|
|
```bash
|
|
RUN_SLOW=1 pytest tests/
|
|
RUN_SLOW=1 pytest examples/
|
|
```
|
|
|
|
๊ฒฐ๊ณผ๋ [์ฌ๊ธฐ](https://github.com/huggingface/transformers/actions)์์ ํ์ธํ ์ ์์ต๋๋ค.
|
|
|
|
|
|
## ํ
์คํธ ์คํ[[running-tests]]
|
|
|
|
|
|
|
|
|
|
|
|
### ์คํํ ํ
์คํธ ์ ํ[[choosing-which-tests-to-run]]
|
|
|
|
์ด ๋ฌธ์๋ ํ
์คํธ๋ฅผ ์คํํ๋ ๋ค์ํ ๋ฐฉ๋ฒ์ ๋ํด ์์ธํ ์ค๋ช
ํฉ๋๋ค.
|
|
๋ชจ๋ ๋ด์ฉ์ ์ฝ์ ํ์๋, ๋ ์์ธํ ๋ด์ฉ์ด ํ์ํ๋ค๋ฉด [์ฌ๊ธฐ](https://docs.pytest.org/en/latest/usage.html)์์ ํ์ธํ ์ ์์ต๋๋ค.
|
|
|
|
๋ค์์ ๊ฐ์ฅ ์ ์ฉํ ํ
์คํธ ์คํ ๋ฐฉ๋ฒ ๋ช ๊ฐ์ง์
๋๋ค.
|
|
|
|
๋ชจ๋ ์คํ:
|
|
|
|
```console
|
|
pytest
|
|
```
|
|
|
|
๋๋:
|
|
|
|
```bash
|
|
make test
|
|
```
|
|
|
|
ํ์๋ ๋ค์๊ณผ ๊ฐ์ด ์ ์๋ฉ๋๋ค:
|
|
|
|
```bash
|
|
python -m pytest -n auto --dist=loadfile -s -v ./tests/
|
|
```
|
|
|
|
์์ ๋ช
๋ น์ด๋ pytest์๊ฒ ์๋์ ๋ด์ฉ์ ์ ๋ฌํฉ๋๋ค:
|
|
|
|
- ์ฌ์ฉ ๊ฐ๋ฅํ CPU ์ฝ์ด ์๋งํผ ํ
์คํธ ํ๋ก์ธ์ค๋ฅผ ์คํํฉ๋๋ค. (RAM์ด ์ถฉ๋ถํ์ง ์๋ค๋ฉด, ํ
์คํธ ํ๋ก์ธ์ค ์๊ฐ ๋๋ฌด ๋ง์ ์ ์์ต๋๋ค!)
|
|
- ๋์ผํ ํ์ผ์ ๋ชจ๋ ํ
์คํธ๋ ๋์ผํ ํ
์คํธ ํ๋ก์ธ์ค์์ ์คํ๋์ด์ผ ํฉ๋๋ค.
|
|
- ์ถ๋ ฅ์ ์บก์ฒํ์ง ์์ต๋๋ค.
|
|
- ์์ธํ ๋ชจ๋๋ก ์คํํฉ๋๋ค.
|
|
|
|
|
|
|
|
### ๋ชจ๋ ํ
์คํธ ๋ชฉ๋ก ๊ฐ์ ธ์ค๊ธฐ[[getting-the-list-of-all-tests]]
|
|
|
|
ํ
์คํธ ์ค์ํธ์ ๋ชจ๋ ํ
์คํธ:
|
|
|
|
```bash
|
|
pytest --collect-only -q
|
|
```
|
|
|
|
์ง์ ๋ ํ
์คํธ ํ์ผ์ ๋ชจ๋ ํ
์คํธ:
|
|
|
|
```bash
|
|
pytest tests/test_optimization.py --collect-only -q
|
|
```
|
|
|
|
### ํน์ ํ
์คํธ ๋ชจ๋ ์คํ[[run-a-specific-test-module]]
|
|
|
|
๊ฐ๋ณ ํ
์คํธ ๋ชจ๋ ์คํํ๊ธฐ:
|
|
|
|
```bash
|
|
pytest tests/utils/test_logging.py
|
|
```
|
|
|
|
### ํน์ ํ
์คํธ ์คํ[[run-specific-tests]]
|
|
|
|
๋๋ถ๋ถ์ ํ
์คํธ ๋ด๋ถ์์๋ unittest๊ฐ ์ฌ์ฉ๋ฉ๋๋ค. ๋ฐ๋ผ์ ํน์ ํ์ ํ
์คํธ๋ฅผ ์คํํ๋ ค๋ฉด ํด๋น ํ
์คํธ๋ฅผ ํฌํจํ๋ unittest ํด๋์ค์ ์ด๋ฆ์ ์์์ผ ํฉ๋๋ค.
|
|
์๋ฅผ ๋ค์ด ๋ค์๊ณผ ๊ฐ์ ์ ์์ต๋๋ค:
|
|
|
|
```bash
|
|
pytest tests/test_optimization.py::OptimizationTest::test_adam_w
|
|
```
|
|
|
|
์์ ๋ช
๋ น์ด์ ์๋ฏธ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
|
|
|
|
- `tests/test_optimization.py` - ํ
์คํธ๊ฐ ์๋ ํ์ผ
|
|
- `OptimizationTest` - ํด๋์ค์ ์ด๋ฆ
|
|
- `test_adam_w` - ํน์ ํ
์คํธ ํจ์์ ์ด๋ฆ
|
|
|
|
ํ์ผ์ ์ฌ๋ฌ ํด๋์ค๊ฐ ํฌํจ๋ ๊ฒฝ์ฐ, ํน์ ํด๋์ค์ ํ
์คํธ๋ง ์คํํ ์๋ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
|
|
|
|
```bash
|
|
pytest tests/test_optimization.py::OptimizationTest
|
|
```
|
|
|
|
์ด ๋ช
๋ น์ด๋ ํด๋น ํด๋์ค ๋ด๋ถ์ ๋ชจ๋ ํ
์คํธ๋ฅผ ์คํํฉ๋๋ค.
|
|
|
|
์์์ ์ธ๊ธํ ๊ฒ์ฒ๋ผ `OptimizationTest` ํด๋์ค์ ํฌํจ๋ ํ
์คํธ๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
|
|
|
|
```bash
|
|
pytest tests/test_optimization.py::OptimizationTest --collect-only -q
|
|
```
|
|
|
|
ํค์๋ ํํ์์ ์ฌ์ฉํ์ฌ ํ
์คํธ๋ฅผ ์คํํ ์๋ ์์ต๋๋ค.
|
|
|
|
`adam`์ด๋ผ๋ ์ด๋ฆ์ ํฌํจํ๋ ํ
์คํธ๋ง ์คํํ๋ ค๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
|
|
|
|
```bash
|
|
pytest -k adam tests/test_optimization.py
|
|
```
|
|
|
|
๋
ผ๋ฆฌ ์ฐ์ฐ์ `and`์ `or`๋ฅผ ์ฌ์ฉํ์ฌ ๋ชจ๋ ํค์๋๊ฐ ์ผ์นํด์ผ ํ๋์ง ๋๋ ์ด๋ ํ๋๊ฐ ์ผ์นํด์ผ ํ๋์ง๋ฅผ ๋ํ๋ผ ์ ์์ต๋๋ค.
|
|
`not`์ ๋ถ์ ํ ๋ ์ฌ์ฉํ ์ ์์ต๋๋ค.
|
|
|
|
`adam`์ด๋ผ๋ ์ด๋ฆ์ ํฌํจํ์ง ์๋ ๋ชจ๋ ํ
์คํธ๋ฅผ ์คํํ๋ ค๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
|
|
|
|
```bash
|
|
pytest -k "not adam" tests/test_optimization.py
|
|
```
|
|
|
|
๋ ๊ฐ์ง ํจํด์ ํ๋๋ก ๊ฒฐํฉํ ์๋ ์์ต๋๋ค:
|
|
|
|
```bash
|
|
pytest -k "ada and not adam" tests/test_optimization.py
|
|
```
|
|
|
|
์๋ฅผ ๋ค์ด `test_adafactor`์ `test_adam_w`๋ฅผ ๋ชจ๋ ์คํํ๋ ค๋ฉด ๋ค์์ ์ฌ์ฉํ ์ ์์ต๋๋ค:
|
|
|
|
```bash
|
|
pytest -k "test_adam_w or test_adam_w" tests/test_optimization.py
|
|
```
|
|
|
|
์ฌ๊ธฐ์ `or`๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ์ ์ํ์ธ์. ๋ ํค์๋ ์ค ํ๋๊ฐ ์ผ์นํ๋๋ก ํ๊ธฐ ์ํ ๋ชฉ์ ์ผ๋ก ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์
๋๋ค.
|
|
|
|
๋ ํจํด์ด ๋ชจ๋ ํฌํจ๋์ด์ผ ํ๋ ํ
์คํธ๋ง ์คํํ๋ ค๋ฉด, `and`๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค:
|
|
|
|
```bash
|
|
pytest -k "test and ada" tests/test_optimization.py
|
|
```
|
|
|
|
### `accelerate` ํ
์คํธ ์คํ[[run-`accelerate`-tests]]
|
|
|
|
๋ชจ๋ธ์์ `accelerate` ํ
์คํธ๋ฅผ ์คํํด์ผ ํ ๋๊ฐ ์์ต๋๋ค. ์ด๋ฅผ ์ํด์๋ ๋ช
๋ น์ด์ `-m accelerate_tests`๋ฅผ ์ถ๊ฐํ๋ฉด ๋ฉ๋๋ค.
|
|
์๋ฅผ ๋ค์ด, `OPT`์์ ์ด๋ฌํ ํ
์คํธ๋ฅผ ์คํํ๋ ค๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
|
|
```bash
|
|
RUN_SLOW=1 pytest -m accelerate_tests tests/models/opt/test_modeling_opt.py
|
|
```
|
|
|
|
### ๋ฌธ์ ํ
์คํธ ์คํ[[run-documentation-tests]]
|
|
|
|
์์ ๋ฌธ์๊ฐ ์ฌ๋ฐ๋ฅธ์ง ํ
์คํธํ๋ ค๋ฉด `doctests`๊ฐ ํต๊ณผํ๋์ง ํ์ธํด์ผ ํฉ๋๋ค.
|
|
์๋ฅผ ๋ค์ด, [`WhisperModel.forward`'s docstring](https://github.com/huggingface/transformers/blob/main/src/transformers/models/whisper/modeling_whisper.py#L1017-L1035)๋ฅผ ์ฌ์ฉํด ๋ด
์๋ค:
|
|
|
|
```python
|
|
r"""
|
|
Returns:
|
|
|
|
Example:
|
|
```python
|
|
>>> import torch
|
|
>>> from transformers import WhisperModel, WhisperFeatureExtractor
|
|
>>> from datasets import load_dataset
|
|
|
|
>>> model = WhisperModel.from_pretrained("openai/whisper-base")
|
|
>>> feature_extractor = WhisperFeatureExtractor.from_pretrained("openai/whisper-base")
|
|
>>> ds = load_dataset("hf-internal-testing/librispeech_asr_dummy", "clean", split="validation")
|
|
>>> inputs = feature_extractor(ds[0]["audio"]["array"], return_tensors="pt")
|
|
>>> input_features = inputs.input_features
|
|
>>> decoder_input_ids = torch.tensor([[1, 1]]) * model.config.decoder_start_token_id
|
|
>>> last_hidden_state = model(input_features, decoder_input_ids=decoder_input_ids).last_hidden_state
|
|
>>> list(last_hidden_state.shape)
|
|
[1, 2, 512]
|
|
```"""
|
|
|
|
```
|
|
|
|
์ํ๋ ํ์ผ์ ๋ชจ๋ docstring ์์ ๋ฅผ ์๋์ผ๋ก ํ
์คํธํ๋ ค๋ฉด ๋ค์ ๋ช
๋ น์ ์คํํ๋ฉด ๋ฉ๋๋ค:
|
|
```bash
|
|
pytest --doctest-modules <path_to_file_or_dir>
|
|
```
|
|
ํ์ผ์ ํ์ฅ์๊ฐ markdown์ธ ๊ฒฝ์ฐ `--doctest-glob="*.md"` ์ธ์๋ฅผ ์ถ๊ฐํด์ผ ํฉ๋๋ค.
|
|
|
|
### ์์ ๋ ํ
์คํธ๋ง ์คํ[[run-only-modified-tests]]
|
|
|
|
์์ ๋ ํ์ผ ๋๋ ํ์ฌ ๋ธ๋์น (Git ๊ธฐ์ค)์ ๊ด๋ จ๋ ํ
์คํธ๋ฅผ ์คํํ๋ ค๋ฉด [pytest-picked](https://github.com/anapaulagomes/pytest-picked)์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
|
|
์ด๋ ๋ณ๊ฒฝํ ๋ด์ฉ์ด ํ
์คํธ์ ์ํฅ์ ์ฃผ์ง ์์๋์ง ๋น ๋ฅด๊ฒ ํ์ธํ ์ ์๋ ์ข์ ๋ฐฉ๋ฒ์
๋๋ค.
|
|
|
|
```bash
|
|
pip install pytest-picked
|
|
```
|
|
|
|
```bash
|
|
pytest --picked
|
|
```
|
|
|
|
์์ ๋์์ง๋ง, ์์ง ์ปค๋ฐ๋์ง ์์ ๋ชจ๋ ํ์ผ ๋ฐ ํด๋์์ ํ
์คํธ๊ฐ ์คํ๋ฉ๋๋ค.
|
|
|
|
### ์์ค ์์ ์ ์คํจํ ํ
์คํธ ์๋ ์ฌ์คํ[[automatically-rerun-failed-tests-on-source-modification]]
|
|
|
|
[pytest-xdist](https://github.com/pytest-dev/pytest-xdist)๋ ๋ชจ๋ ์คํจํ ํ
์คํธ๋ฅผ ๊ฐ์งํ๊ณ ,
|
|
ํ์ผ์ ์์ ํ ํ์ ํ์ผ์ ๊ณ์ ์ฌ์คํํ์ฌ ํ
์คํธ๊ฐ ์ฑ๊ณตํ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ๋ ๋งค์ฐ ์ ์ฉํ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
|
|
๋ฐ๋ผ์ ์์ ํ ๋ด์ฉ์ ํ์ธํ ํ pytest๋ฅผ ๋ค์ ์์ํ ํ์๊ฐ ์์ต๋๋ค.
|
|
๋ชจ๋ ํ
์คํธ๊ฐ ํต๊ณผ๋ ๋๊น์ง ์ด ๊ณผ์ ์ ๋ฐ๋ณตํ ํ ๋ค์ ์ ์ฒด ์คํ์ด ์ด๋ฃจ์ด์ง๋๋ค.
|
|
|
|
```bash
|
|
pip install pytest-xdist
|
|
```
|
|
|
|
์ฌ๊ท์ ๋ชจ๋์ ์ฌ์ฉ: `pytest -f` ๋๋ `pytest --looponfail`
|
|
|
|
ํ์ผ์ ๋ณ๊ฒฝ ์ฌํญ์ `looponfailroots` ๋ฃจํธ ๋๋ ํฐ๋ฆฌ์ ํด๋น ๋ด์ฉ์ (์ฌ๊ท์ ์ผ๋ก) ํ์ธํ์ฌ ๊ฐ์ง๋ฉ๋๋ค.
|
|
์ด ๊ฐ์ ๊ธฐ๋ณธ๊ฐ์ด ์๋ํ์ง ์๋ ๊ฒฝ์ฐ,
|
|
`setup.cfg`์ ์ค์ ์ต์
์ ๋ณ๊ฒฝํ์ฌ ํ๋ก์ ํธ์์ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค:
|
|
|
|
```ini
|
|
[tool:pytest]
|
|
looponfailroots = transformers tests
|
|
```
|
|
|
|
๋๋ `pytest.ini`/`tox.ini`` ํ์ผ:
|
|
|
|
```ini
|
|
[pytest]
|
|
looponfailroots = transformers tests
|
|
```
|
|
|
|
์ด๋ ๊ฒ ํ๋ฉด ini-file์ ๋๋ ํฐ๋ฆฌ๋ฅผ ๊ธฐ์ค์ผ๋ก ์๋์ ์ผ๋ก ์ง์ ๋ ๊ฐ ๋๋ ํฐ๋ฆฌ์์ ํ์ผ ๋ณ๊ฒฝ ์ฌํญ๋ง ์ฐพ๊ฒ ๋ฉ๋๋ค.
|
|
|
|
|
|
์ด ๊ธฐ๋ฅ์ ๋์ฒดํ ์ ์๋ ๊ตฌํ ๋ฐฉ๋ฒ์ธ [pytest-watch](https://github.com/joeyespo/pytest-watch)๋ ์์ต๋๋ค.
|
|
|
|
|
|
### ํน์ ํ
์คํธ ๋ชจ๋ ๊ฑด๋๋ฐ๊ธฐ[[skip-a-test-module]]
|
|
|
|
๋ชจ๋ ํ
์คํธ ๋ชจ๋์ ์คํํ๋ ํน์ ๋ชจ๋์ ์ ์ธํ๋ ค๋ฉด, ์คํํ ํ
์คํธ ๋ชฉ๋ก์ ๋ช
์์ ์ผ๋ก ์ง์ ํ ์ ์์ต๋๋ค.
|
|
์๋ฅผ ๋ค์ด, `test_modeling_*.py` ํ
์คํธ๋ฅผ ์ ์ธํ ๋ชจ๋ ํ
์คํธ๋ฅผ ์คํํ๋ ค๋ฉด ๋ค์์ ์ฌ์ฉํ ์ ์์ต๋๋ค:
|
|
|
|
```bash
|
|
pytest *ls -1 tests/*py | grep -v test_modeling*
|
|
```
|
|
|
|
### ์ํ ์ด๊ธฐํ[[clearing state]]
|
|
|
|
CI ๋น๋ ๋ฐ (์๋์ ๋ํ) ๊ฒฉ๋ฆฌ๊ฐ ์ค์ํ ๊ฒฝ์ฐ, ์บ์๋ฅผ ์ง์์ผ ํฉ๋๋ค:
|
|
|
|
```bash
|
|
pytest --cache-clear tests
|
|
```
|
|
|
|
### ํ
์คํธ๋ฅผ ๋ณ๋ ฌ๋ก ์คํ[[running-tests-in-parallel]]
|
|
|
|
์ด์ ์ ์ธ๊ธํ ๊ฒ์ฒ๋ผ `make test`๋ ํ
์คํธ๋ฅผ ๋ณ๋ ฌ๋ก ์คํํ๊ธฐ ์ํด
|
|
`pytest-xdist` ํ๋ฌ๊ทธ์ธ(`-n X` ์ธ์, ์๋ฅผ ๋ค์ด `-n 2`๋ฅผ ์ฌ์ฉํ์ฌ 2๊ฐ์ ๋ณ๋ ฌ ์์
์คํ)์ ํตํด ์คํ๋ฉ๋๋ค.
|
|
|
|
`pytest-xdist`์ `--dist=` ์ต์
์ ์ฌ์ฉํ์ฌ ํ
์คํธ๋ฅผ ์ด๋ป๊ฒ ๊ทธ๋ฃนํํ ์ง ์ ์ดํ ์ ์์ต๋๋ค.
|
|
`--dist=loadfile`์ ํ๋์ ํ์ผ์ ์๋ ํ
์คํธ๋ฅผ ๋์ผํ ํ๋ก์ธ์ค๋ก ๊ทธ๋ฃนํํฉ๋๋ค.
|
|
|
|
์คํ๋ ํ
์คํธ์ ์์๊ฐ ๋ค๋ฅด๊ณ ์์ธกํ ์ ์๊ธฐ ๋๋ฌธ์, `pytest-xdist`๋ก ํ
์คํธ ์ค์ํธ๋ฅผ ์คํํ๋ฉด ์คํจ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค (๊ฒ์ถ๋์ง ์์ ๊ฒฐํฉ๋ ํ
์คํธ๊ฐ ์๋ ๊ฒฝ์ฐ).
|
|
์ด ๊ฒฝ์ฐ [pytest-replay](https://github.com/ESSS/pytest-replay)๋ฅผ ์ฌ์ฉํ๋ฉด ๋์ผํ ์์๋ก ํ
์คํธ๋ฅผ ๋ค์ ์คํํด์
|
|
์คํจํ๋ ์ํ์ค๋ฅผ ์ต์ํํ๋ ๋ฐ์ ๋์์ด ๋ฉ๋๋ค.
|
|
|
|
### ํ
์คํธ ์์์ ๋ฐ๋ณต[[test-order-and-repetition]]
|
|
|
|
์ ์ฌ์ ์ธ ์ข
์์ฑ ๋ฐ ์ํ ๊ด๋ จ ๋ฒ๊ทธ(tear down)๋ฅผ ๊ฐ์งํ๊ธฐ ์ํด
|
|
ํ
์คํธ๋ฅผ ์ฌ๋ฌ ๋ฒ, ์ฐ์์ผ๋ก, ๋ฌด์์๋ก ๋๋ ์ธํธ๋ก ๋ฐ๋ณตํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
|
|
๊ทธ๋ฆฌ๊ณ ์ง์ ์ ์ธ ์ฌ๋ฌ ๋ฒ์ ๋ฐ๋ณต์ DL์ ๋ฌด์์์ฑ์ ์ํด ๋ฐ๊ฒฌ๋๋ ์ผ๋ถ ๋ฌธ์ ๋ฅผ ๊ฐ์งํ๋ ๋ฐ์๋ ์ ์ฉํฉ๋๋ค.
|
|
|
|
|
|
#### ํ
์คํธ๋ฅผ ๋ฐ๋ณต[[repeat-tests]]
|
|
|
|
- [pytest-flakefinder](https://github.com/dropbox/pytest-flakefinder):
|
|
|
|
```bash
|
|
pip install pytest-flakefinder
|
|
```
|
|
|
|
๋ชจ๋ ํ
์คํธ๋ฅผ ์ฌ๋ฌ ๋ฒ ์คํํฉ๋๋ค(๊ธฐ๋ณธ๊ฐ์ 50๋ฒ):
|
|
|
|
```bash
|
|
pytest --flake-finder --flake-runs=5 tests/test_failing_test.py
|
|
```
|
|
|
|
<Tip>
|
|
|
|
์ด ํ๋ฌ๊ทธ์ธ์ `pytest-xdist`์ `-n` ํ๋๊ทธ์ ํจ๊ป ์๋ํ์ง ์์ต๋๋ค.
|
|
|
|
</Tip>
|
|
|
|
<Tip>
|
|
|
|
`pytest-repeat`๋ผ๋ ๋ ๋ค๋ฅธ ํ๋ฌ๊ทธ์ธ๋ ์์ง๋ง `unittest`์ ํจ๊ป ์๋ํ์ง ์์ต๋๋ค.
|
|
|
|
</Tip>
|
|
|
|
#### ํ
์คํธ๋ฅผ ์์์ ์์๋ก ์คํ[[run-tests-in-a-random-order]]
|
|
|
|
```bash
|
|
pip install pytest-random-order
|
|
```
|
|
|
|
์ค์: `pytest-random-order`๊ฐ ์ค์น๋๋ฉด ํ
์คํธ๊ฐ ์๋์ผ๋ก ์์์ ์์๋ก ์์
๋๋ค.
|
|
๊ตฌ์ฑ ๋ณ๊ฒฝ์ด๋ ์ปค๋งจ๋ ๋ผ์ธ ์ต์
์ด ํ์ํ์ง ์์ต๋๋ค.
|
|
|
|
์์ ์ค๋ช
ํ ๊ฒ์ฒ๋ผ ์ด๋ฅผ ํตํด ํ ํ
์คํธ์ ์ํ๊ฐ ๋ค๋ฅธ ํ
์คํธ์ ์ํ์ ์ํฅ์ ๋ฏธ์น๋ ๊ฒฐํฉ๋ ํ
์คํธ๋ฅผ ๊ฐ์งํ ์ ์์ต๋๋ค.
|
|
`pytest-random-order`๊ฐ ์ค์น๋๋ฉด ํด๋น ์ธ์
์์ ์ฌ์ฉ๋ ๋๋ค ์๋๊ฐ ์ถ๋ ฅ๋๋ฉฐ ์๋ฅผ ๋ค์ด ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
|
|
|
|
```bash
|
|
pytest tests
|
|
[...]
|
|
Using --random-order-bucket=module
|
|
Using --random-order-seed=573663
|
|
```
|
|
|
|
๋ฐ๋ผ์ ํน์ ์ํ์ค๊ฐ ์คํจํ๋ ๊ฒฝ์ฐ์๋ ์ ํํ ์๋๋ฅผ ์ถ๊ฐํ์ฌ ์ฌํํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
|
|
|
|
```bash
|
|
pytest --random-order-seed=573663
|
|
[...]
|
|
Using --random-order-bucket=module
|
|
Using --random-order-seed=573663
|
|
```
|
|
|
|
์ ํํ ๋์ผํ ํ
์คํธ ๋ชฉ๋ก(๋๋ ๋ชฉ๋ก์ด ์์)์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ์๋ง ์ ํํ ์์๋ฅผ ์ฌํํฉ๋๋ค.
|
|
๋ชฉ๋ก์ ์๋์ผ๋ก ์ขํ๊ธฐ ์์ํ๋ฉด ๋ ์ด์ ์๋์ ์์กดํ ์ ์๊ณ ์คํจํ๋ ์ ํํ ์์๋ก ์๋์ผ๋ก ๋ชฉ๋ก์ ๋์ดํด์ผํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ `--random-order-bucket=none`์ ์ฌ์ฉํ์ฌ pytest์๊ฒ ์์๋ฅผ ์์๋ก ์ค์ ํ์ง ์๋๋ก ์๋ ค์ผ ํฉ๋๋ค.
|
|
์๋ฅผ ๋ค์ด ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
|
|
|
|
```bash
|
|
pytest --random-order-bucket=none tests/test_a.py tests/test_c.py tests/test_b.py
|
|
```
|
|
|
|
๋ชจ๋ ํ
์คํธ์ ๋ํด ์๊ธฐ๋ฅผ ๋นํ์ฑํํ๋ ค๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
|
|
|
|
```bash
|
|
pytest --random-order-bucket=none
|
|
```
|
|
|
|
๊ธฐ๋ณธ์ ์ผ๋ก `--random-order-bucket=module`์ด ๋ด์ฌ๋์ด ์์ผ๋ฏ๋ก, ๋ชจ๋ ์์ค์์ ํ์ผ์ ์์ต๋๋ค.
|
|
๋ํ `class`, `package`, `global` ๋ฐ `none` ์์ค์์๋ ์์ ์ ์์ต๋๋ค.
|
|
์์ธํ ๋ด์ฉ์ ํด๋น [๋ฌธ์](https://github.com/jbasko/pytest-random-order)๋ฅผ ์ฐธ์กฐํ์ธ์.
|
|
|
|
๋ ๋ค๋ฅธ ๋ฌด์์ํ์ ๋์์ [`pytest-randomly`](https://github.com/pytest-dev/pytest-randomly)์
๋๋ค.
|
|
์ด ๋ชจ๋์ ๋งค์ฐ ์ ์ฌํ ๊ธฐ๋ฅ/์ธํฐํ์ด์ค๋ฅผ ๊ฐ์ง๊ณ ์์ง๋ง, `pytest-random-order`์ ์๋ ๋ฒํท ๋ชจ๋๋ฅผ ์ฌ์ฉํ ์๋ ์์ต๋๋ค.
|
|
์ค์น ํ์๋ ์๋์ผ๋ก ์ ์ฉ๋๋ ๋ฌธ์ ๋ ๋์ผํ๊ฒ ๊ฐ์ง๋๋ค.
|
|
|
|
### ์ธ๊ด๊ณผ ๋๋์ ๋ณ๊ฒฝ[[look-and-feel-variations]
|
|
|
|
#### pytest-sugar ์ฌ์ฉ[[pytest-sugar]]
|
|
|
|
[pytest-sugar](https://github.com/Frozenball/pytest-sugar)๋ ํ
์คํธ๊ฐ ๋ณด์ฌ์ง๋ ํํ๋ฅผ ๊ฐ์ ํ๊ณ ,
|
|
์งํ ์ํฉ ๋ฐ๋ฅผ ์ถ๊ฐํ๋ฉฐ, ์คํจํ ํ
์คํธ์ ๊ฒ์ฆ์ ์ฆ์ ํ์ํ๋ ํ๋ฌ๊ทธ์ธ์
๋๋ค. ์ค์นํ๋ฉด ์๋์ผ๋ก ํ์ฑํ๋ฉ๋๋ค.
|
|
|
|
```bash
|
|
pip install pytest-sugar
|
|
```
|
|
|
|
pytest-sugar ์์ด ํ
์คํธ๋ฅผ ์คํํ๋ ค๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
|
|
|
|
```bash
|
|
pytest -p no:sugar
|
|
```
|
|
|
|
๋๋ ์ ๊ฑฐํ์ธ์.
|
|
|
|
|
|
|
|
#### ๊ฐ ํ์ ํ
์คํธ ์ด๋ฆ๊ณผ ์งํ ์ํฉ ๋ณด๊ณ [[report-each-sub-test-name-and-its-progress]]
|
|
|
|
`pytest`๋ฅผ ํตํด ๋จ์ผ ๋๋ ๊ทธ๋ฃน์ ํ
์คํธ๋ฅผ ์คํํ๋ ๊ฒฝ์ฐ(`pip install pytest-pspec` ์ดํ):
|
|
|
|
```bash
|
|
pytest --pspec tests/test_optimization.py
|
|
```
|
|
|
|
#### ์คํจํ ํ
์คํธ ์ฆ์ ํ์[[instantly-shows-failed-tests]]
|
|
|
|
[pytest-instafail](https://github.com/pytest-dev/pytest-instafail)์ ํ
์คํธ ์ธ์
์ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ์ง ์๊ณ
|
|
์คํจ ๋ฐ ์ค๋ฅ๋ฅผ ์ฆ์ ํ์ํฉ๋๋ค.
|
|
|
|
```bash
|
|
pip install pytest-instafail
|
|
```
|
|
|
|
```bash
|
|
pytest --instafail
|
|
```
|
|
|
|
### GPU ์ฌ์ฉ ์ฌ๋ถ[[to-GPU-or-not-to-GPU]]
|
|
|
|
GPU๊ฐ ํ์ฑํ๋ ํ๊ฒฝ์์, CPU ์ ์ฉ ๋ชจ๋๋ก ํ
์คํธํ๋ ค๋ฉด `CUDA_VISIBLE_DEVICES=""`๋ฅผ ์ถ๊ฐํฉ๋๋ค:
|
|
|
|
```bash
|
|
CUDA_VISIBLE_DEVICES="" pytest tests/utils/test_logging.py
|
|
```
|
|
|
|
๋๋ ๋ค์ค GPU๊ฐ ์๋ ๊ฒฝ์ฐ `pytest`์์ ์ฌ์ฉํ GPU๋ฅผ ์ง์ ํ ์๋ ์์ต๋๋ค.
|
|
์๋ฅผ ๋ค์ด, GPU `0` ๋ฐ `1`์ด ์๋ ๊ฒฝ์ฐ ๋ค์์ ์คํํ ์ ์์ต๋๋ค:
|
|
|
|
```bash
|
|
CUDA_VISIBLE_DEVICES="1" pytest tests/utils/test_logging.py
|
|
```
|
|
|
|
์ด๋ ๊ฒ ํ๋ฉด ๋ค๋ฅธ GPU์์ ๋ค๋ฅธ ์์
์ ์คํํ๋ ค๋ ๊ฒฝ์ฐ ์ ์ฉํฉ๋๋ค.
|
|
|
|
์ผ๋ถ ํ
์คํธ๋ ๋ฐ๋์ CPU ์ ์ฉ์ผ๋ก ์คํํด์ผ ํ๋ฉฐ, ์ผ๋ถ๋ CPU ๋๋ GPU ๋๋ TPU์์ ์คํํด์ผ ํ๊ณ , ์ผ๋ถ๋ ์ฌ๋ฌ GPU์์ ์คํํด์ผ ํฉ๋๋ค.
|
|
๋ค์ ์คํต ๋ฐ์ฝ๋ ์ดํฐ๋ ํ
์คํธ์ ์๊ตฌ ์ฌํญ์ CPU/GPU/TPU๋ณ๋ก ์ค์ ํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค:
|
|
|
|
- `require_torch` - ์ด ํ
์คํธ๋ torch์์๋ง ์คํ๋ฉ๋๋ค.
|
|
- `require_torch_gpu` - `require_torch`์ ์ถ๊ฐ๋ก ์ ์ด๋ 1๊ฐ์ GPU๊ฐ ํ์ํฉ๋๋ค.
|
|
- `require_torch_multi_gpu` - `require_torch`์ ์ถ๊ฐ๋ก ์ ์ด๋ 2๊ฐ์ GPU๊ฐ ํ์ํฉ๋๋ค.
|
|
- `require_torch_non_multi_gpu` - `require_torch`์ ์ถ๊ฐ๋ก 0๊ฐ ๋๋ 1๊ฐ์ GPU๊ฐ ํ์ํฉ๋๋ค.
|
|
- `require_torch_up_to_2_gpus` - `require_torch`์ ์ถ๊ฐ๋ก 0๊ฐ, 1๊ฐ ๋๋ 2๊ฐ์ GPU๊ฐ ํ์ํฉ๋๋ค.
|
|
- `require_torch_xla` - `require_torch`์ ์ถ๊ฐ๋ก ์ ์ด๋ 1๊ฐ์ TPU๊ฐ ํ์ํฉ๋๋ค.
|
|
|
|
GPU ์๊ตฌ ์ฌํญ์ ํ๋ก ์ ๋ฆฌํ๋ฉด ์๋์ ๊ฐ์ต๋๋ใ
:
|
|
|
|
|
|
| n gpus | decorator |
|
|
|--------+--------------------------------|
|
|
| `>= 0` | `@require_torch` |
|
|
| `>= 1` | `@require_torch_gpu` |
|
|
| `>= 2` | `@require_torch_multi_gpu` |
|
|
| `< 2` | `@require_torch_non_multi_gpu` |
|
|
| `< 3` | `@require_torch_up_to_2_gpus` |
|
|
|
|
|
|
์๋ฅผ ๋ค์ด, 2๊ฐ ์ด์์ GPU๊ฐ ์๊ณ pytorch๊ฐ ์ค์น๋์ด ์์ ๋์๋ง ์คํ๋์ด์ผ ํ๋ ํ
์คํธ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
|
|
|
|
```python no-style
|
|
@require_torch_multi_gpu
|
|
def test_example_with_multi_gpu():
|
|
```
|
|
|
|
์ด๋ฌํ ๋ฐ์ฝ๋ ์ดํฐ๋ ์ค์ฒฉ๋ ์ ์์ต๋๋ค.
|
|
์๋ฅผ ๋ค์ด, ๋๋ฆฐ ํ
์คํธ๋ก ์งํ๋๊ณ pytorch์์ ์ ์ด๋ ํ๋์ GPU๊ฐ ํ์ํ ๊ฒฝ์ฐ ๋ค์๊ณผ ๊ฐ์ด ์ค์ ํ ์ ์์ต๋๋ค:
|
|
|
|
```python no-style
|
|
@require_torch_gpu
|
|
@slow
|
|
def test_example_slow_on_gpu():
|
|
```
|
|
|
|
`@parametrized`์ ๊ฐ์ ์ผ๋ถ ๋ฐ์ฝ๋ ์ดํฐ๋ ํ
์คํธ ์ด๋ฆ์ ๋ค์ ์์ฑํ๊ธฐ ๋๋ฌธ์ `@require_*` ์คํต ๋ฐ์ฝ๋ ์ดํฐ๋ ์ฌ๋ฐ๋ฅด๊ฒ ์๋ํ๋ ค๋ฉด ํญ์ ๋งจ ๋ง์ง๋ง์ ๋์ด๋์ด์ผ ํฉ๋๋ค.
|
|
๋ค์์ ์ฌ๋ฐ๋ฅธ ์ฌ์ฉ ์์
๋๋ค:
|
|
|
|
```python no-style
|
|
@parameterized.expand(...)
|
|
@require_torch_multi_gpu
|
|
def test_integration_foo():
|
|
```
|
|
|
|
`@pytest.mark.parametrize`์๋ ์ด๋ฌํ ์์ ๋ฌธ์ ๋ ์์ผ๋ฏ๋ก ์ฒ์ ํน์ ๋ง์ง๋ง์ ์์น์ํฌ ์ ์๊ณ ์ด๋ฌํ ๊ฒฝ์ฐ์๋ ์ ์๋ํ ๊ฒ์
๋๋ค.
|
|
ํ์ง๋ง unittest๊ฐ ์๋ ๊ฒฝ์ฐ์๋ง ์๋ํฉ๋๋ค.
|
|
|
|
ํ
์คํธ ๋ด๋ถ์์ ๋ค์์ ์ฌ์ฉํ ์ ์์ต๋๋ค:
|
|
|
|
- ์ฌ์ฉ ๊ฐ๋ฅํ GPU ์:
|
|
|
|
```python
|
|
from transformers.testing_utils import get_gpu_count
|
|
|
|
n_gpu = get_gpu_count() #torch์ tf์ ํจ๊ป ์๋
|
|
```
|
|
|
|
### ๋ถ์ฐ ํ๋ จ[[distributed-training]]
|
|
|
|
`pytest`๋ ๋ถ์ฐ ํ๋ จ์ ์ง์ ์ ์ผ๋ก ๋ค๋ฃจ์ง ๋ชปํฉ๋๋ค.
|
|
์ด๋ฅผ ์๋ํ๋ฉด ํ์ ํ๋ก์ธ์ค๊ฐ ์ฌ๋ฐ๋ฅธ ์์
์ ์ํํ์ง ์๊ณ `pytest`๋ผ๊ณ ์๊ฐํ๊ธฐ์ ํ
์คํธ ์ค์ํธ๋ฅผ ๋ฐ๋ณตํด์ ์คํํ๊ฒ ๋ฉ๋๋ค.
|
|
๊ทธ๋ฌ๋ ์ผ๋ฐ ํ๋ก์ธ์ค๋ฅผ ์์ฑํ ๋ค์ ์ฌ๋ฌ ์์ปค๋ฅผ ์์ฑํ๊ณ IO ํ์ดํ๋ฅผ ๊ด๋ฆฌํ๋๋ก ํ๋ฉด ๋์ํฉ๋๋ค.
|
|
|
|
๋ค์์ ์ฌ์ฉ ๊ฐ๋ฅํ ํ
์คํธ์
๋๋ค:
|
|
|
|
- [test_trainer_distributed.py](https://github.com/huggingface/transformers/tree/main/tests/trainer/distributed/test_trainer_distributed.py)
|
|
- [test_deepspeed.py](https://github.com/huggingface/transformers/tree/main/tests/deepspeed/test_deepspeed.py)
|
|
|
|
์คํ ์ง์ ์ผ๋ก ๋ฐ๋ก ์ด๋ํ๋ ค๋ฉด, ํด๋น ํ
์คํธ์์ `execute_subprocess_async` ํธ์ถ์ ๊ฒ์ํ์ธ์.
|
|
|
|
์ด๋ฌํ ํ
์คํธ๋ฅผ ์คํํ๋ ค๋ฉด ์ ์ด๋ 2๊ฐ์ GPU๊ฐ ํ์ํฉ๋๋ค.
|
|
|
|
```bash
|
|
CUDA_VISIBLE_DEVICES=0,1 RUN_SLOW=1 pytest -sv tests/test_trainer_distributed.py
|
|
```
|
|
|
|
### ์ถ๋ ฅ ์บก์ฒ[[output-capture]]
|
|
|
|
ํ
์คํธ ์คํ ์ค `stdout` ๋ฐ `stderr`๋ก ์ ์ก๋ ๋ชจ๋ ์ถ๋ ฅ์ด ์บก์ฒ๋ฉ๋๋ค.
|
|
ํ
์คํธ๋ ์ค์ ๋ฉ์๋๊ฐ ์คํจํ๋ฉด ์บก์ฒ๋ ์ถ๋ ฅ์ ์ผ๋ฐ์ ์ผ๋ก ์คํจ ์ถ์ ์ ๋ณด์ ํจ๊ป ํ์๋ฉ๋๋ค.
|
|
|
|
์ถ๋ ฅ ์บก์ฒ๋ฅผ ๋นํ์ฑํํ๊ณ `stdout` ๋ฐ `stderr`๋ฅผ ์ ์์ ์ผ๋ก ๋ฐ์ผ๋ ค๋ฉด `-s` ๋๋ `--capture=no`๋ฅผ ์ฌ์ฉํ์ธ์:
|
|
|
|
```bash
|
|
pytest -s tests/utils/test_logging.py
|
|
```
|
|
|
|
ํ
์คํธ ๊ฒฐ๊ณผ๋ฅผ JUnit ํ์์ ์ถ๋ ฅ์ผ๋ก ๋ณด๋ด๋ ค๋ฉด ๋ค์์ ์ฌ์ฉํ์ธ์:
|
|
|
|
```bash
|
|
py.test tests --junitxml=result.xml
|
|
```
|
|
|
|
### ์์ ์กฐ์ [[color-control]]
|
|
|
|
์์์ด ์๊ฒ ํ๋ ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ค์ ํ์ธ์(์๋ฅผ ๋ค์ด ํฐ์ ๋ฐฐ๊ฒฝ์ ๋
ธ๋์ ๊ธ์จ๋ ๊ฐ๋
์ฑ์ด ์ข์ง ์์ต๋๋ค):
|
|
|
|
```bash
|
|
pytest --color=no tests/utils/test_logging.py
|
|
```
|
|
|
|
### online pastebin service์ ํ
์คํธ ๋ณด๊ณ ์ ์ ์ก[[sending test report to online pastebin service]]
|
|
|
|
๊ฐ ํ
์คํธ ์คํจ์ ๋ํ URL์ ๋ง๋ญ๋๋ค:
|
|
|
|
```bash
|
|
pytest --pastebin=failed tests/utils/test_logging.py
|
|
```
|
|
|
|
์ด๋ ๊ฒ ํ๋ฉด ๊ฐ ์คํจ์ ๋ํ URL์ ์ ๊ณตํ๋ remote Paste service์ ํ
์คํธ ์คํ ์ ๋ณด๋ฅผ ์ ์ถํฉ๋๋ค.
|
|
์ผ๋ฐ์ ์ธ ํ
์คํธ๋ฅผ ์ ํํ ์๋ ์๊ณ ํน์ ํน์ ์คํจ๋ง ๋ณด๋ด๋ ค๋ฉด `-x`์ ๊ฐ์ด ์ถ๊ฐํ ์๋ ์์ต๋๋ค.
|
|
|
|
์ ์ฒด ํ
์คํธ ์ธ์
๋ก๊ทธ์ ๋ํ URL์ ์์ฑํฉ๋๋ค:
|
|
|
|
```bash
|
|
pytest --pastebin=all tests/utils/test_logging.py
|
|
```
|
|
|
|
## ํ
์คํธ ์์ฑ[[writing-tests]]
|
|
|
|
๐ค transformers ํ
์คํธ๋ ๋๋ถ๋ถ `unittest`๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ์ง๋ง,
|
|
`pytest`์์ ์คํ๋๋ฏ๋ก ๋๋ถ๋ถ์ ๊ฒฝ์ฐ ๋ ์์คํ
์ ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
|
|
|
|
์ง์๋๋ ๊ธฐ๋ฅ์ ๋ํด [์ฌ๊ธฐ](https://docs.pytest.org/en/stable/unittest.html)์์ ํ์ธํ ์ ์์ง๋ง,
|
|
๊ธฐ์ตํด์ผ ํ ์ค์ํ ์ ์ ๋๋ถ๋ถ์ `pytest` fixture๊ฐ ์๋ํ์ง ์๋๋ค๋ ๊ฒ์
๋๋ค.
|
|
ํ๋ผ๋ฏธํฐํ๋ ์๋ํ์ง ์์ง๋ง, ์ฐ๋ฆฌ๋ ๋น์ทํ ๋ฐฉ์์ผ๋ก ์๋ํ๋ `parameterized` ๋ชจ๋์ ์ฌ์ฉํฉ๋๋ค.
|
|
|
|
|
|
### ๋งค๊ฐ๋ณ์ํ[[parametrization]]
|
|
|
|
๋์ผํ ํ
์คํธ๋ฅผ ๋ค๋ฅธ ์ธ์๋ก ์ฌ๋ฌ ๋ฒ ์คํํด์ผ ํ๋ ๊ฒฝ์ฐ๊ฐ ์ข
์ข
์์ต๋๋ค.
|
|
ํ
์คํธ ๋ด์์ ์ด ์์
์ ์ํํ ์ ์์ง๋ง, ๊ทธ๋ ๊ฒ ํ๋ฉด ํ๋์ ์ธ์ ์ธํธ์ ๋ํด ํ
์คํธ๋ฅผ ์คํํ ์ ์์ต๋๋ค.
|
|
|
|
```python
|
|
# test_this1.py
|
|
import unittest
|
|
from parameterized import parameterized
|
|
|
|
|
|
class TestMathUnitTest(unittest.TestCase):
|
|
@parameterized.expand(
|
|
[
|
|
("negative", -1.5, -2.0),
|
|
("integer", 1, 1.0),
|
|
("large fraction", 1.6, 1),
|
|
]
|
|
)
|
|
def test_floor(self, name, input, expected):
|
|
assert_equal(math.floor(input), expected)
|
|
```
|
|
|
|
์ด์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ด ํ
์คํธ๋ `test_floor`์ ๋ง์ง๋ง 3๊ฐ ์ธ์๊ฐ
|
|
๋งค๊ฐ๋ณ์ ๋ชฉ๋ก์ ํด๋น ์ธ์์ ํ ๋น๋๋ ๊ฒ์ผ๋ก 3๋ฒ ์คํ๋ ๊ฒ์
๋๋ค.
|
|
|
|
๊ทธ๋ฆฌ๊ณ `negative` ๋ฐ `integer` ๋งค๊ฐ๋ณ์ ์งํฉ๋ง ์คํํ๋ ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์คํํ ์ ์์ต๋๋ค:
|
|
|
|
```bash
|
|
pytest -k "negative and integer" tests/test_mytest.py
|
|
```
|
|
|
|
๋๋ `negative` ํ์ ํ
์คํธ๋ฅผ ์ ์ธํ ๋ชจ๋ ์๋ธ ํ
์คํธ๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ์คํํ ์ ์์ต๋๋ค:
|
|
|
|
```bash
|
|
pytest -k "not negative" tests/test_mytest.py
|
|
```
|
|
|
|
์์์ ์ธ๊ธํ `-k` ํํฐ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ ์ธ์๋,
|
|
๊ฐ ์๋ธ ํ
์คํธ์ ์ ํํ ์ด๋ฆ์ ํ์ธํ ํ์ ์ผ๋ถ ํน์ ์ ์ฒด ์๋ธ ํ
์คํธ๋ฅผ ์คํํ ์ ์์ต๋๋ค.
|
|
|
|
```bash
|
|
pytest test_this1.py --collect-only -q
|
|
```
|
|
|
|
๊ทธ๋ฆฌ๊ณ ๋ค์์ ๋ด์ฉ์ ํ์ธํ ์ ์์ ๊ฒ์
๋๋ค:
|
|
|
|
```bash
|
|
test_this1.py::TestMathUnitTest::test_floor_0_negative
|
|
test_this1.py::TestMathUnitTest::test_floor_1_integer
|
|
test_this1.py::TestMathUnitTest::test_floor_2_large_fraction
|
|
```
|
|
|
|
2๊ฐ์ ํน์ ํ ์๋ธ ํ
์คํธ๋ง ์คํํ ์๋ ์์ต๋๋ค:
|
|
|
|
```bash
|
|
pytest test_this1.py::TestMathUnitTest::test_floor_0_negative test_this1.py::TestMathUnitTest::test_floor_1_integer
|
|
```
|
|
|
|
`transformers`์ ๊ฐ๋ฐ์ ์ข
์์ฑ์ ์ด๋ฏธ ์๋ [parameterized](https://pypi.org/project/parameterized/) ๋ชจ๋์
|
|
`unittests`์ `pytest` ํ
์คํธ ๋ชจ๋์์ ์๋ํฉ๋๋ค.
|
|
|
|
๊ทธ๋ฌ๋ ํ
์คํธ๊ฐ `unittest`๊ฐ ์๋ ๊ฒฝ์ฐ `pytest.mark.parametrize`๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค(์ด๋ฏธ ์๋ ์ผ๋ถ ํ
์คํธ์์ ์ฌ์ฉ๋๋ ๊ฒฝ์ฐ๋ ์์ต๋๋ค.
|
|
์ฃผ๋ก `examples` ํ์์ ์์ต๋๋ค).
|
|
|
|
๋ค์์ `pytest`์ `parametrize` ๋ง์ปค๋ฅผ ์ฌ์ฉํ ๋์ผํ ์์
๋๋ค:
|
|
|
|
```python
|
|
# test_this2.py
|
|
import pytest
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"name, input, expected",
|
|
[
|
|
("negative", -1.5, -2.0),
|
|
("integer", 1, 1.0),
|
|
("large fraction", 1.6, 1),
|
|
],
|
|
)
|
|
def test_floor(name, input, expected):
|
|
assert_equal(math.floor(input), expected)
|
|
```
|
|
|
|
`parameterized`์ ๋ง์ฐฌ๊ฐ์ง๋ก `pytest.mark.parametrize`๋ฅผ ์ฌ์ฉํ๋ฉด
|
|
`-k` ํํฐ๊ฐ ์๋ํ์ง ์๋ ๊ฒฝ์ฐ์๋ ์คํํ ์๋ธ ํ
์คํธ๋ฅผ ์ ํํ๊ฒ ์ง์ ํ ์ ์์ต๋๋ค.
|
|
๋จ, ์ด ๋งค๊ฐ๋ณ์ํ ํจ์๋ ์๋ธ ํ
์คํธ์ ์ด๋ฆ ์งํฉ์ ์ฝ๊ฐ ๋ค๋ฅด๊ฒ ์์ฑํฉ๋๋ค. ๋ค์๊ณผ ๊ฐ์ ๋ชจ์ต์
๋๋ค:
|
|
|
|
```bash
|
|
pytest test_this2.py --collect-only -q
|
|
```
|
|
|
|
๊ทธ๋ฆฌ๊ณ ๋ค์์ ๋ด์ฉ์ ํ์ธํ ์ ์์ ๊ฒ์
๋๋ค:
|
|
|
|
```bash
|
|
test_this2.py::test_floor[integer-1-1.0]
|
|
test_this2.py::test_floor[negative--1.5--2.0]
|
|
test_this2.py::test_floor[large fraction-1.6-1]
|
|
```
|
|
|
|
ํน์ ํ ํ
์คํธ์ ๋ํด์๋ง ์คํํ ์๋ ์์ต๋๋ค:
|
|
|
|
```bash
|
|
pytest test_this2.py::test_floor[negative--1.5--2.0] test_this2.py::test_floor[integer-1-1.0]
|
|
```
|
|
|
|
์ด์ ์ ์์์ ๊ฐ์ด ์คํํ ์ ์์ต๋๋ค.
|
|
|
|
|
|
|
|
### ํ์ผ ๋ฐ ๋๋ ํฐ๋ฆฌ[[files-and-directories]]
|
|
|
|
ํ
์คํธ์์ ์ข
์ข
ํ์ฌ ํ
์คํธ ํ์ผ๊ณผ ๊ด๋ จ๋ ์๋์ ์ธ ์์น๋ฅผ ์์์ผ ํ๋ ๊ฒฝ์ฐ๊ฐ ์์ต๋๋ค.
|
|
ํ
์คํธ๊ฐ ์ฌ๋ฌ ๋๋ ํฐ๋ฆฌ์์ ํธ์ถ๋๊ฑฐ๋ ๊น์ด๊ฐ ๋ค๋ฅธ ํ์ ๋๋ ํฐ๋ฆฌ์ ์์ ์ ์๊ธฐ ๋๋ฌธ์ ๊ทธ ์์น๋ฅผ ์๋ ๊ฒ์ ๊ฐ๋จํ์ง ์์ต๋๋ค.
|
|
`transformers.test_utils.TestCasePlus`๋ผ๋ ํฌํผ ํด๋์ค๋ ๋ชจ๋ ๊ธฐ๋ณธ ๊ฒฝ๋ก๋ฅผ ์ฒ๋ฆฌํ๊ณ ๊ฐ๋จํ ์ก์ธ์๋ฅผ ์ ๊ณตํ์ฌ ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํฉ๋๋ค:
|
|
|
|
|
|
- `pathlib` ๊ฐ์ฒด(์์ ํ ์ ํด์ง ๊ฒฝ๋ก)
|
|
|
|
- `test_file_path` - ํ์ฌ ํ
์คํธ ํ์ผ ๊ฒฝ๋ก (์: `__file__`)
|
|
- test_file_dir` - ํ์ฌ ํ
์คํธ ํ์ผ์ด ํฌํจ๋ ๋๋ ํฐ๋ฆฌ
|
|
- tests_dir` - `tests` ํ
์คํธ ์ค์ํธ์ ๋๋ ํฐ๋ฆฌ
|
|
- examples_dir` - `examples` ํ
์คํธ ์ค์ํธ์ ๋๋ ํฐ๋ฆฌ
|
|
- repo_root_dir` - ์ ์ฅ์ ๋๋ ํฐ๋ฆฌ
|
|
- src_dir` - `src`์ ๋๋ ํฐ๋ฆฌ(์: `transformers` ํ์ ๋๋ ํฐ๋ฆฌ๊ฐ ์๋ ๊ณณ)
|
|
|
|
- ๋ฌธ์์ด๋ก ๋ณํ๋ ๊ฒฝ๋ก---์์ ๋์ผํ์ง๋ง, `pathlib` ๊ฐ์ฒด๊ฐ ์๋ ๋ฌธ์์ด๋ก ๊ฒฝ๋ก๋ฅผ ๋ฐํํฉ๋๋ค:
|
|
|
|
- `test_file_path_str`
|
|
- `test_file_dir_str`
|
|
- `tests_dir_str`
|
|
- `examples_dir_str`
|
|
- `repo_root_dir_str`
|
|
- `src_dir_str`
|
|
|
|
์์ ๋ด์ฉ์ ์ฌ์ฉํ๋ ค๋ฉด ํ
์คํธ๊ฐ 'transformers.test_utils.TestCasePlus'์ ์๋ธํด๋์ค์ ์๋์ง ํ์ธํด์ผ ํฉ๋๋ค.
|
|
์๋ฅผ ๋ค์ด ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
|
|
|
|
```python
|
|
from transformers.testing_utils import TestCasePlus
|
|
|
|
|
|
class PathExampleTest(TestCasePlus):
|
|
def test_something_involving_local_locations(self):
|
|
data_dir = self.tests_dir / "fixtures/tests_samples/wmt_en_ro"
|
|
```
|
|
|
|
๋ง์ฝ `pathlib`๋ฅผ ํตํด ๊ฒฝ๋ก๋ฅผ ์กฐ์ํ ํ์๊ฐ ์๊ฑฐ๋ ๊ฒฝ๋ก๋ฅผ ๋ฌธ์์ด๋ก๋ง ํ์๋ก ํ๋ ๊ฒฝ์ฐ์๋ `pathlib` ๊ฐ์ฒด์ `str()`์ ํธ์ถํ๊ฑฐ๋ `_str`๋ก ๋๋๋ ์ ๊ทผ์๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
|
|
์๋ฅผ ๋ค์ด ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
|
|
|
|
```python
|
|
from transformers.testing_utils import TestCasePlus
|
|
|
|
|
|
class PathExampleTest(TestCasePlus):
|
|
def test_something_involving_stringified_locations(self):
|
|
examples_dir = self.examples_dir_str
|
|
```
|
|
|
|
### ์์ ํ์ผ ๋ฐ ๋๋ ํฐ๋ฆฌ[[temporary-files-and-directories]]
|
|
|
|
๊ณ ์ ํ ์์ ํ์ผ ๋ฐ ๋๋ ํฐ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๋ณ๋ ฌ ํ
์คํธ ์คํ์ ์์ด ํ์์ ์
๋๋ค.
|
|
์ด๋ ๊ฒ ํจ์ผ๋ก์จ ํ
์คํธ๋ค์ด ์๋ก์ ๋ฐ์ดํฐ๋ฅผ ๋ฎ์ด์ฐ์ง ์๊ฒ ํ ์ ์์ต๋๋ค. ๋ํ ์ฐ๋ฆฌ๋ ์์ฑ๋ ํ
์คํธ์ ์ข
๋ฃ ๋จ๊ณ์์ ์ด๋ฌํ ์์ ํ์ผ ๋ฐ ๋๋ ํฐ๋ฆฌ๋ฅผ ์ ๊ฑฐํ๊ณ ์ถ์ต๋๋ค.
|
|
๋ฐ๋ผ์ ์ด๋ฌํ ์๊ตฌ ์ฌํญ์ ์ถฉ์กฑ์์ผ์ฃผ๋ `tempfile`๊ณผ ๊ฐ์ ํจํค์ง๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค.
|
|
|
|
๊ทธ๋ฌ๋ ํ
์คํธ๋ฅผ ๋๋ฒ๊น
ํ ๋๋ ์์ ํ์ผ์ด๋ ๋๋ ํฐ๋ฆฌ์ ๋ค์ด๊ฐ๋ ๋ด์ฉ์ ํ์ธํ ์ ์์ด์ผ ํ๋ฉฐ,
|
|
์ฌ์คํ๋๋ ๊ฐ ํ
์คํธ๋ง๋ค ์์ ํ์ผ์ด๋ ๋๋ ํฐ๋ฆฌ์ ๊ฒฝ๋ก์ ๋ํด ๋ฌด์์ ๊ฐ์ด ์๋ ์ ํํ ๊ฐ์ ์๊ณ ์ถ์ ๊ฒ์
๋๋ค.
|
|
|
|
`transformers.test_utils.TestCasePlus`๋ผ๋ ๋์ฐ๋ฏธ ํด๋์ค๋ ์ด๋ฌํ ๋ชฉ์ ์ ๊ฐ์ฅ ์ ํฉํฉ๋๋ค.
|
|
์ด ํด๋์ค๋ `unittest.TestCase`์ ํ์ ํด๋์ค์ด๋ฏ๋ก, ์ฐ๋ฆฌ๋ ์ด๊ฒ์ ํ
์คํธ ๋ชจ๋์์ ์ฝ๊ฒ ์์ํ ์ ์์ต๋๋ค.
|
|
|
|
๋ค์์ ํด๋น ํด๋์ค๋ฅผ ์ฌ์ฉํ๋ ์์์
๋๋ค:
|
|
|
|
```python
|
|
from transformers.testing_utils import TestCasePlus
|
|
|
|
|
|
class ExamplesTests(TestCasePlus):
|
|
def test_whatever(self):
|
|
tmp_dir = self.get_auto_remove_tmp_dir()
|
|
```
|
|
|
|
์ด ์ฝ๋๋ ๊ณ ์ ํ ์์ ๋๋ ํฐ๋ฆฌ๋ฅผ ์์ฑํ๊ณ `tmp_dir`์ ํด๋น ์์น๋ก ์ค์ ํฉ๋๋ค.
|
|
|
|
- ๊ณ ์ ํ ์์ ๋๋ ํฐ๋ฆฌ๋ฅผ ์์ฑํฉ๋๋ค:
|
|
|
|
```python
|
|
def test_whatever(self):
|
|
tmp_dir = self.get_auto_remove_tmp_dir()
|
|
```
|
|
|
|
`tmp_dir`์๋ ์์ฑ๋ ์์ ๋๋ ํฐ๋ฆฌ์ ๊ฒฝ๋ก๊ฐ ํฌํจ๋ฉ๋๋ค.
|
|
์ด๋ ํ
์คํธ์ ์ข
๋ฃ ๋จ๊ณ์์ ์๋์ผ๋ก ์ ๊ฑฐ๋ฉ๋๋ค.
|
|
|
|
- ์ ํํ ๊ฒฝ๋ก๋ก ์์ ๋๋ ํฐ๋ฆฌ ์์ฑ ํ์ ํ
์คํธ ์์ ์ ์ ๋น์ด ์๋ ์ํ์ธ์ง ํ์ธํ๊ณ , ํ
์คํธ ํ์๋ ๋น์ฐ์ง ๋ง์ธ์.
|
|
|
|
```python
|
|
def test_whatever(self):
|
|
tmp_dir = self.get_auto_remove_tmp_dir("./xxx")
|
|
```
|
|
|
|
์ด๊ฒ์ ๋๋ฒ๊น
ํ ๋ ํน์ ๋๋ ํฐ๋ฆฌ๋ฅผ ๋ชจ๋ํฐ๋งํ๊ณ ,
|
|
๊ทธ ๋๋ ํฐ๋ฆฌ์ ์ด์ ์ ์คํ๋ ํ
์คํธ๊ฐ ๋ฐ์ดํฐ๋ฅผ ๋จ๊ธฐ์ง ์๋๋ก ํ๋ ๋ฐ์ ์ ์ฉํฉ๋๋ค.
|
|
|
|
- `before` ๋ฐ `after` ์ธ์๋ฅผ ์ง์ ์ค๋ฒ๋ผ์ด๋ฉํ์ฌ ๊ธฐ๋ณธ ๋์์ ๋ณ๊ฒฝํ ์ ์์ผ๋ฉฐ
|
|
๋ค์ ์ค ํ๋์ ๋์์ผ๋ก ์ด์ด์ง๋๋ค:
|
|
|
|
- `before=True`: ํ
์คํธ ์์ ์ ์์ ๋๋ ํฐ๋ฆฌ๊ฐ ํญ์ ์ง์์ง๋๋ค.
|
|
- `before=False`: ์์ ๋๋ ํฐ๋ฆฌ๊ฐ ์ด๋ฏธ ์กด์ฌํ๋ ๊ฒฝ์ฐ ๊ธฐ์กด ํ์ผ์ ๊ทธ๋๋ก ๋จ์ต๋๋ค.
|
|
- `after=True`: ํ
์คํธ ์ข
๋ฃ ์ ์์ ๋๋ ํฐ๋ฆฌ๊ฐ ํญ์ ์ญ์ ๋ฉ๋๋ค.
|
|
- `after=False`: ํ
์คํธ ์ข
๋ฃ ์ ์์ ๋๋ ํฐ๋ฆฌ๊ฐ ํญ์ ๊ทธ๋๋ก ์ ์ง๋ฉ๋๋ค.
|
|
|
|
<Tip>
|
|
|
|
`rm -r`์ ํด๋นํ๋ ๋ช
๋ น์ ์์ ํ๊ฒ ์คํํ๊ธฐ ์ํด,
|
|
๋ช
์์ ์ธ `tmp_dir`์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ํ๋ก์ ํธ ์ ์ฅ์ ์ฒดํฌ ์์์ ํ์ ๋๋ ํฐ๋ฆฌ๋ง ํ์ฉ๋ฉ๋๋ค.
|
|
๋ฐ๋ผ์ ์ค์๋ก `/tmp`๊ฐ ์๋ ์ค์ํ ํ์ผ ์์คํ
์ ์ผ๋ถ๊ฐ ์ญ์ ๋์ง ์๋๋ก ํญ์ `./`๋ก ์์ํ๋ ๊ฒฝ๋ก๋ฅผ ์ ๋ฌํด์ผ ํฉ๋๋ค.
|
|
|
|
</Tip>
|
|
|
|
<Tip>
|
|
|
|
๊ฐ ํ
์คํธ๋ ์ฌ๋ฌ ๊ฐ์ ์์ ๋๋ ํฐ๋ฆฌ๋ฅผ ๋ฑ๋กํ ์ ์์ผ๋ฉฐ,
|
|
๋ณ๋๋ก ์์ฒญํ์ง ์๋ ํ ๋ชจ๋ ์๋์ผ๋ก ์ ๊ฑฐ๋ฉ๋๋ค.
|
|
|
|
</Tip>
|
|
|
|
### ์์ sys.path ์ค๋ฒ๋ผ์ด๋[[temporary-sys.path-override]]
|
|
|
|
`sys.path`๋ฅผ ๋ค๋ฅธ ํ
์คํธ๋ก ์์๋ก ์ค๋ฒ๋ผ์ด๋ํ๊ธฐ ์ํด ์๋ฅผ ๋ค์ด `ExtendSysPath` ์ปจํ
์คํธ ๊ด๋ฆฌ์๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
|
|
์๋ฅผ ๋ค์ด ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
|
|
|
|
|
|
```python
|
|
import os
|
|
from transformers.testing_utils import ExtendSysPath
|
|
|
|
bindir = os.path.abspath(os.path.dirname(__file__))
|
|
with ExtendSysPath(f"{bindir}/.."):
|
|
from test_trainer import TrainerIntegrationCommon # noqa
|
|
```
|
|
|
|
### ํ
์คํธ ๊ฑด๋๋ฐ๊ธฐ[[skipping-tests]]
|
|
|
|
์ด๊ฒ์ ๋ฒ๊ทธ๊ฐ ๋ฐ๊ฒฌ๋์ด ์๋ก์ด ํ
์คํธ๊ฐ ์์ฑ๋์์ง๋ง ์์ง ๊ทธ ๋ฒ๊ทธ๊ฐ ์์ ๋์ง ์์ ๊ฒฝ์ฐ์ ์ ์ฉํฉ๋๋ค.
|
|
์ด ํ
์คํธ๋ฅผ ์ฃผ ์ ์ฅ์์ ์ปค๋ฐํ๋ ค๋ฉด `make test` ์ค์ ๊ฑด๋๋ฐ๋๋ก ํด์ผ ํฉ๋๋ค.
|
|
|
|
๋ฐฉ๋ฒ:
|
|
|
|
- **skip**์ ํ
์คํธ๊ฐ ์ผ๋ถ ์กฐ๊ฑด์ด ์ถฉ์กฑ๋ ๊ฒฝ์ฐ์๋ง ํต๊ณผ๋ ๊ฒ์ผ๋ก ์์๋๊ณ , ๊ทธ๋ ์ง ์์ผ๋ฉด pytest๊ฐ ์ ์ฒด ํ
์คํธ๋ฅผ ๊ฑด๋๋ฐ์ด์ผ ํจ์ ์๋ฏธํฉ๋๋ค.
|
|
์ผ๋ฐ์ ์ธ ์๋ก๋ Windows๊ฐ ์๋ ํ๋ซํผ์์ Windows ์ ์ฉ ํ
์คํธ๋ฅผ ๊ฑด๋๋ฐ๊ฑฐ๋
|
|
์ธ๋ถ ๋ฆฌ์์ค(์๋ฅผ ๋ค์ด ๋ฐ์ดํฐ๋ฒ ์ด์ค)์ ์์กดํ๋ ํ
์คํธ๋ฅผ ๊ฑด๋๋ฐ๋ ๊ฒ์ด ์์ต๋๋ค.
|
|
|
|
- **xfail**์ ํ
์คํธ๊ฐ ํน์ ํ ์ด์ ๋ก ์ธํด ์คํจํ ๊ฒ์ผ๋ก ์์ํ๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค.
|
|
์ผ๋ฐ์ ์ธ ์๋ก๋ ์์ง ๊ตฌํ๋์ง ์์ ๊ธฐ๋ฅ์ด๋ ์์ง ์์ ๋์ง ์์ ๋ฒ๊ทธ์ ํ
์คํธ๊ฐ ์์ต๋๋ค.
|
|
`xfail`๋ก ํ์๋ ํ
์คํธ๊ฐ ์์๋๋ก ์คํจํ์ง ์๊ณ ํต๊ณผ๋ ๊ฒฝ์ฐ, ์ด๊ฒ์ xpass์ด๋ฉฐ ํ
์คํธ ๊ฒฐ๊ณผ ์์ฝ์ ๊ธฐ๋ก๋ฉ๋๋ค.
|
|
|
|
๋ ๊ฐ์ง ์ค์ํ ์ฐจ์ด์ ์ค ํ๋๋ `skip`์ ํ
์คํธ๋ฅผ ์คํํ์ง ์์ง๋ง `xfail`์ ์คํํ๋ค๋ ๊ฒ์
๋๋ค.
|
|
๋ฐ๋ผ์ ์ค๋ฅ๊ฐ ์๋ ์ฝ๋๊ฐ ์ผ๋ถ ํ
์คํธ์ ์ํฅ์ ๋ฏธ์น ์ ์๋ ๊ฒฝ์ฐ `xfail`์ ์ฌ์ฉํ์ง ๋ง์ธ์.
|
|
|
|
#### ๊ตฌํ[[implementation]]
|
|
|
|
- ์ ์ฒด ํ
์คํธ๋ฅผ ๋ฌด์กฐ๊ฑด ๊ฑด๋๋ฐ๋ ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด ํ ์ ์์ต๋๋ค:
|
|
|
|
```python no-style
|
|
@unittest.skip(reason="this bug needs to be fixed")
|
|
def test_feature_x():
|
|
```
|
|
|
|
๋๋ pytest๋ฅผ ํตํด:
|
|
|
|
```python no-style
|
|
@pytest.mark.skip(reason="this bug needs to be fixed")
|
|
```
|
|
|
|
๋๋ `xfail` ๋ฐฉ์์ผ๋ก:
|
|
|
|
```python no-style
|
|
@pytest.mark.xfail
|
|
def test_feature_x():
|
|
```
|
|
|
|
- ํ
์คํธ ๋ด๋ถ์์ ๋ด๋ถ ํ์ธ์ ๋ฐ๋ผ ํ
์คํธ๋ฅผ ๊ฑด๋๋ฐ๋ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
|
|
|
|
```python
|
|
def test_feature_x():
|
|
if not has_something():
|
|
pytest.skip("unsupported configuration")
|
|
```
|
|
|
|
๋๋ ๋ชจ๋ ์ ์ฒด:
|
|
|
|
```python
|
|
import pytest
|
|
|
|
if not pytest.config.getoption("--custom-flag"):
|
|
pytest.skip("--custom-flag is missing, skipping tests", allow_module_level=True)
|
|
```
|
|
|
|
๋๋ `xfail` ๋ฐฉ์์ผ๋ก:
|
|
|
|
```python
|
|
def test_feature_x():
|
|
pytest.xfail("expected to fail until bug XYZ is fixed")
|
|
```
|
|
|
|
- import๊ฐ missing๋ ๋ชจ๋์ด ์์ ๋ ๊ทธ ๋ชจ๋์ ๋ชจ๋ ํ
์คํธ๋ฅผ ๊ฑด๋๋ฐ๋ ๋ฐฉ๋ฒ:
|
|
|
|
```python
|
|
docutils = pytest.importorskip("docutils", minversion="0.3")
|
|
```
|
|
|
|
- ์กฐ๊ฑด์ ๋ฐ๋ผ ํ
์คํธ๋ฅผ ๊ฑด๋๋ฐ๋ ๋ฐฉ๋ฒ:
|
|
|
|
```python no-style
|
|
@pytest.mark.skipif(sys.version_info < (3,6), reason="requires python3.6 or higher")
|
|
def test_feature_x():
|
|
```
|
|
|
|
๋๋:
|
|
|
|
```python no-style
|
|
@unittest.skipIf(torch_device == "cpu", "Can't do half precision")
|
|
def test_feature_x():
|
|
```
|
|
|
|
๋๋ ๋ชจ๋ ์ ์ฒด๋ฅผ ๊ฑด๋๋ฐ๋ ๋ฐฉ๋ฒ:
|
|
|
|
```python no-style
|
|
@pytest.mark.skipif(sys.platform == 'win32', reason="does not run on windows")
|
|
class TestClass():
|
|
def test_feature_x(self):
|
|
```
|
|
|
|
๋ณด๋ค ์์ธํ ์์ ๋ฐ ๋ฐฉ๋ฒ์ [์ฌ๊ธฐ](https://docs.pytest.org/en/latest/skipping.html)์์ ํ์ธํ ์ ์์ต๋๋ค.
|
|
|
|
### ๋๋ฆฐ ํ
์คํธ[[slow-tests]]
|
|
|
|
ํ
์คํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์ง์์ ์ผ๋ก ํ์ฅ๋๊ณ ์์ผ๋ฉฐ, ์ผ๋ถ ํ
์คํธ๋ ์คํํ๋ ๋ฐ ๋ช ๋ถ์ด ๊ฑธ๋ฆฝ๋๋ค.
|
|
๊ทธ๋ฆฌ๊ณ ์ฐ๋ฆฌ์๊ฒ๋ ํ
์คํธ ์ค์ํธ๊ฐ CI๋ฅผ ํตํด ์๋ฃ๋๊ธฐ๊น์ง ํ ์๊ฐ์ ๊ธฐ๋ค๋ฆด ์ฌ์ ๊ฐ ์์ต๋๋ค.
|
|
๋ฐ๋ผ์ ํ์ ํ
์คํธ๋ฅผ ์ํ ์ผ๋ถ ์์ธ๋ฅผ ์ ์ธํ๊ณ ๋๋ฆฐ ํ
์คํธ๋ ๋ค์๊ณผ ๊ฐ์ด ํ์ํด์ผ ํฉ๋๋ค.
|
|
|
|
```python no-style
|
|
from transformers.testing_utils import slow
|
|
@slow
|
|
def test_integration_foo():
|
|
```
|
|
|
|
`@slow`๋ก ํ์๋ ํ
์คํธ๋ฅผ ์คํํ๋ ค๋ฉด `RUN_SLOW=1` ํ๊ฒฝ ๋ณ์๋ฅผ ์ค์ ํ์ธ์. ์๋ฅผ ๋ค์ด ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
|
|
|
|
```bash
|
|
RUN_SLOW=1 pytest tests
|
|
```
|
|
|
|
`@parameterized`์ ๊ฐ์ ๋ช ๊ฐ์ง ๋ฐ์ฝ๋ ์ดํฐ๋ ํ
์คํธ ์ด๋ฆ์ ๋ค์ ์์ฑํฉ๋๋ค.
|
|
๊ทธ๋ฌ๋ฏ๋ก `@slow`์ ๋๋จธ์ง ๊ฑด๋๋ฐ๊ธฐ ๋ฐ์ฝ๋ ์ดํฐ `@require_*`๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ ์๋๋๋ ค๋ฉด ๋ง์ง๋ง์ ๋์ด๋์ด์ผ ํฉ๋๋ค. ๋ค์์ ์ฌ๋ฐ๋ฅธ ์ฌ์ฉ ์์
๋๋ค.
|
|
|
|
```python no-style
|
|
@parameterized.expand(...)
|
|
@slow
|
|
def test_integration_foo():
|
|
```
|
|
|
|
์ด ๋ฌธ์์ ์ด๋ฐ๋ถ์ ์ค๋ช
๋ ๊ฒ์ฒ๋ผ ๋๋ฆฐ ํ
์คํธ๋ PR์ CI ํ์ธ์ด ์๋ ์์ฝ๋ ์ผ์ ๊ธฐ๋ฐ์ผ๋ก ์คํ๋ฉ๋๋ค.
|
|
๋ฐ๋ผ์ PR ์ ์ถ ์ค์ ์ผ๋ถ ๋ฌธ์ ๋ฅผ ๋์น ์ฑ๋ก ๋ณํฉ๋ ์ ์์ต๋๋ค.
|
|
์ด๋ฌํ ๋ฌธ์ ๋ค์ ๋ค์๋ฒ์ ์์ ๋ CI ์์
์ค์ ๊ฐ์ง๋ฉ๋๋ค.
|
|
ํ์ง๋ง PR์ ์ ์ถํ๊ธฐ ์ ์ ์์ ์ ์ปดํจํฐ์์ ๋๋ฆฐ ํ
์คํธ๋ฅผ ์คํํ๋ ๊ฒ ๋ํ ์ค์ํฉ๋๋ค.
|
|
|
|
๋๋ฆฐ ํ
์คํธ๋ก ํ์ํด์ผ ํ๋์ง ์ฌ๋ถ๋ฅผ ๊ฒฐ์ ํ๋ ๋๋ต์ ์ธ ๊ฒฐ์ ๊ธฐ์ค์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
|
|
|
|
๋ง์ฝ ํ
์คํธ๊ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ด๋ถ ๊ตฌ์ฑ ์์ ์ค ํ๋์ ์ง์ค๋์ด ์๋ค๋ฉด(์: ๋ชจ๋ธ๋ง ํ์ผ, ํ ํฐํ ํ์ผ, ํ์ดํ๋ผ์ธ),
|
|
ํด๋น ํ
์คํธ๋ฅผ ๋๋ฆฐ ํ
์คํธ ์ค์ํธ์์ ์คํํด์ผ ํฉ๋๋ค.
|
|
๋ง์ฝ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ค๋ฅธ ์ธก๋ฉด(์: ๋ฌธ์ ๋๋ ์์ )์ ์ง์ค๋์ด ์๋ค๋ฉด,
|
|
ํด๋น ํ
์คํธ๋ฅผ ๋๋ฆฐ ํ
์คํธ ์ค์ํธ์์ ์คํํด์ผ ํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ด ์ ๊ทผ ๋ฐฉ์์ ๋ณด์ํ๊ธฐ ์ํด ์์ธ๋ฅผ ๋ง๋ค์ด์ผ ํฉ๋๋ค.
|
|
|
|
- ๋ฌด๊ฑฐ์ด ๊ฐ์ค์น ์ธํธ๋ 50MB๋ณด๋ค ํฐ ๋ฐ์ดํฐ์
์ ๋ค์ด๋ก๋ํด์ผ ํ๋ ๋ชจ๋ ํ
์คํธ(์: ๋ชจ๋ธ ํตํฉ ํ
์คํธ, ํ ํฌ๋์ด์ ํตํฉ ํ
์คํธ, ํ์ดํ๋ผ์ธ ํตํฉ ํ
์คํธ)๋ฅผ
|
|
๋๋ฆฐ ํ
์คํธ๋ก ์ค์ ํด์ผ ํฉ๋๋ค.
|
|
์๋ก์ด ๋ชจ๋ธ์ ์ถ๊ฐํ๋ ๊ฒฝ์ฐ ํตํฉ ํ
์คํธ์ฉ์ผ๋ก ๋ฌด์์ ๊ฐ์ค์น๋ก ์์ ๋ฒ์ ์ ๋ง๋ค์ด ํ๋ธ์ ์
๋ก๋ํด์ผ ํฉ๋๋ค.
|
|
์ด ๋ด์ฉ์ ์๋ ๋จ๋ฝ์์ ์ค๋ช
๋ฉ๋๋ค.
|
|
- ํน๋ณํ ๋น ๋ฅด๊ฒ ์คํ๋๋๋ก ์ต์ ํ๋์ง ์์ ํ์ต์ ์ํํด์ผ ํ๋ ํ
์คํธ๋ ๋๋ฆฐ ํ
์คํธ๋ก ์ค์ ํด์ผ ํฉ๋๋ค.
|
|
- ๋๋ฆฌ์ง ์์์ผ ํ ํ
์คํธ ์ค ์ผ๋ถ๊ฐ ๊ทน๋๋ก ๋๋ฆฐ ๊ฒฝ์ฐ
|
|
์์ธ๋ฅผ ๋์
ํ๊ณ ์ด๋ฅผ `@slow`๋ก ์ค์ ํ ์ ์์ต๋๋ค.
|
|
๋์ฉ๋ ํ์ผ์ ๋์คํฌ์ ์ ์ฅํ๊ณ ๋ถ๋ฌ์ค๋ ์๋ ๋ชจ๋ธ๋ง ํ
์คํธ๋ `@slow`์ผ๋ก ํ์๋ ํ
์คํธ์ ์ข์ ์์
๋๋ค.
|
|
- CI์์ 1์ด ์ด๋ด์ ํ
์คํธ๊ฐ ์๋ฃ๋๋ ๊ฒฝ์ฐ(๋ค์ด๋ก๋ ํฌํจ)์๋ ๋๋ฆฐ ํ
์คํธ๊ฐ ์๋์ด์ผ ํฉ๋๋ค.
|
|
|
|
๋๋ฆฐ ํ
์คํธ๊ฐ ์๋ ๊ฒฝ์ฐ์๋ ๋ค์ํ ๋ด๋ถ๋ฅผ ์์ ํ ์ปค๋ฒํ๋ฉด์ ๋น ๋ฅด๊ฒ ์ ์ง๋์ด์ผ ํฉ๋๋ค.
|
|
์๋ฅผ ๋ค์ด, ๋ฌด์์ ๊ฐ์ค์น๋ฅผ ์ฌ์ฉํ์ฌ ํน๋ณํ ์์ฑ๋ ์์ ๋ชจ๋ธ๋ก ํ
์คํธํ๋ฉด ์๋นํ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ์ป์ ์ ์์ต๋๋ค.
|
|
์ด๋ฌํ ๋ชจ๋ธ์ ์ต์ํ์ ๋ ์ด์ด ์(์: 2), ์ดํ ํฌ๊ธฐ(์: 1000) ๋ฑ์ ์์๋ง ๊ฐ์ง๋๋ค. ๊ทธ๋ฐ ๋ค์ `@slow` ํ
์คํธ๋ ๋ํ ๋๋ฆฐ ๋ชจ๋ธ์ ์ฌ์ฉํ์ฌ ์ ์ฑ์ ์ธ ํ
์คํธ๋ฅผ ์ํํ ์ ์์ต๋๋ค.
|
|
์ด๋ฌํ ์์ ๋ชจ๋ธ์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ํ์ธํ๋ ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด *tiny* ๋ชจ๋ธ์ ์ฐพ์๋ณด์ธ์.
|
|
|
|
```bash
|
|
grep tiny tests examples
|
|
```
|
|
|
|
๋ค์์ ์์ ๋ชจ๋ธ[stas/tiny-wmt19-en-de](https://huggingface.co/stas/tiny-wmt19-en-de)์ ๋ง๋
|
|
[script](https://github.com/huggingface/transformers/tree/main/scripts/fsmt/fsmt-make-tiny-model.py) ์์์
๋๋ค.
|
|
ํน์ ๋ชจ๋ธ์ ์ํคํ
์ฒ์ ๋ง๊ฒ ์ฝ๊ฒ ์กฐ์ ํ ์ ์์ต๋๋ค.
|
|
|
|
์๋ฅผ ๋ค์ด ๋์ฉ๋ ๋ชจ๋ธ์ ๋ค์ด๋ก๋ํ๋ ๊ฒฝ์ฐ ๋ฐํ์์ ์๋ชป ์ธก์ ํ๊ธฐ ์ฝ์ง๋ง,
|
|
๋ก์ปฌ์์ ํ
์คํธํ๋ฉด ๋ค์ด๋ก๋ํ ํ์ผ์ด ์บ์๋์ด ๋ค์ด๋ก๋ ์๊ฐ์ด ์ธก์ ๋์ง ์์ต๋๋ค.
|
|
๋์ CI ๋ก๊ทธ์ ์คํ ์๋ ๋ณด๊ณ ์๋ฅผ ํ์ธํ์ธ์(`pytest --durations=0 tests`์ ์ถ๋ ฅ).
|
|
|
|
์ด ๋ณด๊ณ ์๋ ๋๋ฆฐ ์ด์๊ฐ์ผ๋ก ํ์๋์ง ์๊ฑฐ๋ ๋น ๋ฅด๊ฒ ๋ค์ ์์ฑํด์ผ ํ๋ ๋๋ฆฐ ์ด์๊ฐ์ ์ฐพ๋ ๋ฐ๋ ์ ์ฉํฉ๋๋ค.
|
|
CI์์ ํ
์คํธ ์ค์ํธ๊ฐ ๋๋ ค์ง๊ธฐ ์์ํ๋ฉด ์ด ๋ณด๊ณ ์์ ๋งจ ์ ๋ชฉ๋ก์ ๊ฐ์ฅ ๋๋ฆฐ ํ
์คํธ๊ฐ ํ์๋ฉ๋๋ค.
|
|
|
|
|
|
|
|
### stdout/stderr ์ถ๋ ฅ ํ
์คํธ[[testing-the-stdout/stderr-output]]
|
|
|
|
`stdout` ๋ฐ/๋๋ `stderr`๋ก ์ฐ๋ ํจ์๋ฅผ ํ
์คํธํ๋ ค๋ฉด `pytest`์ [capsys ์์คํ
](https://docs.pytest.org/en/latest/capture.html)์ ์ฌ์ฉํ์ฌ ํด๋น ์คํธ๋ฆผ์ ์ก์ธ์คํ ์ ์์ต๋๋ค.
|
|
๋ค์๊ณผ ๊ฐ์ด ์ํํ ์ ์์ต๋๋ค.
|
|
|
|
```python
|
|
import sys
|
|
|
|
|
|
def print_to_stdout(s):
|
|
print(s)
|
|
|
|
|
|
def print_to_stderr(s):
|
|
sys.stderr.write(s)
|
|
|
|
|
|
def test_result_and_stdout(capsys):
|
|
msg = "Hello"
|
|
print_to_stdout(msg)
|
|
print_to_stderr(msg)
|
|
out, err = capsys.readouterr() # ์บก์ฒ๋ ์ถ๋ ฅ ์คํธ๋ฆผ ์ฌ์ฉ
|
|
# ์ ํ ์ฌํญ: ์บก์ฒ๋ ์คํธ๋ฆผ ์ฌ์์ฑ
|
|
sys.stdout.write(out)
|
|
sys.stderr.write(err)
|
|
# ํ
์คํธ:
|
|
assert msg in out
|
|
assert msg in err
|
|
```
|
|
|
|
๊ทธ๋ฆฌ๊ณ , ๋ฌผ๋ก ๋๋ถ๋ถ์ ๊ฒฝ์ฐ์๋ `stderr`๋ ์์ธ์ ์ผ๋ถ๋ก ์ ๊ณต๋ฉ๋๋ค.
|
|
๊ทธ๋ฌ๋ฏ๋ก ํด๋น ๊ฒฝ์ฐ์๋ try/except๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค.
|
|
|
|
```python
|
|
def raise_exception(msg):
|
|
raise ValueError(msg)
|
|
|
|
|
|
def test_something_exception():
|
|
msg = "Not a good value"
|
|
error = ""
|
|
try:
|
|
raise_exception(msg)
|
|
except Exception as e:
|
|
error = str(e)
|
|
assert msg in error, f"{msg} is in the exception:\n{error}"
|
|
```
|
|
|
|
`stdout`๋ฅผ ์บก์ฒํ๋ ๋ ๋ค๋ฅธ ๋ฐฉ๋ฒ์ `contextlib.redirect_stdout`๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์
๋๋ค.
|
|
|
|
```python
|
|
from io import StringIO
|
|
from contextlib import redirect_stdout
|
|
|
|
|
|
def print_to_stdout(s):
|
|
print(s)
|
|
|
|
|
|
def test_result_and_stdout():
|
|
msg = "Hello"
|
|
buffer = StringIO()
|
|
with redirect_stdout(buffer):
|
|
print_to_stdout(msg)
|
|
out = buffer.getvalue()
|
|
# ์ ํ ์ฌํญ: ์บก์ฒ๋ ์คํธ๋ฆผ ์ฌ์์ฑ
|
|
sys.stdout.write(out)
|
|
# ํ
์คํธ:
|
|
assert msg in out
|
|
```
|
|
|
|
`stdout` ์บก์ฒ์ ๊ด๋ จ๋ ์ค์ํ ๋ฌธ์ ์ค ํ๋๋ ๋ณดํต `print`์์ ์ด์ ์ ์ธ์๋ ๋ด์ฉ์ ์ฌ์ค์ ํ๋ `\r` ๋ฌธ์๊ฐ ํฌํจ๋ ์ ์๋ค๋ ๊ฒ์
๋๋ค.
|
|
`pytest`์์๋ ๋ฌธ์ ๊ฐ ์์ง๋ง `pytest -s`์์๋ ์ด๋ฌํ ๋ฌธ์๊ฐ ๋ฒํผ์ ํฌํจ๋๋ฏ๋ก
|
|
`-s`๊ฐ ์๊ฑฐ๋ ์๋ ์ํ์์ ํ์คํธ๋ฅผ ์ํํ ์ ์์ผ๋ ค๋ฉด ์บก์ฒ๋ ์ถ๋ ฅ์ ๋ํด ์ถ๊ฐ์ ์ธ ์ ๋ฆฌ๊ฐ ํ์ํฉ๋๋ค.
|
|
์ด ๊ฒฝ์ฐ์๋ `re.sub(r'~.*\r', '', buf, 0, re.M)`์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
|
|
|
|
ํ์ง๋ง ๋์ฐ๋ฏธ ์ปจํ
์คํธ ๊ด๋ฆฌ์ ๋ํผ๋ฅผ ์ฌ์ฉํ๋ฉด
|
|
์ถ๋ ฅ์ `\r`์ด ํฌํจ๋์ด ์๋์ง์ ์ฌ๋ถ์ ๊ด๊ณ์์ด ๋ชจ๋ ๊ฒ์ ์๋์ผ๋ก ์ฒ๋ฆฌํ๋ฏ๋ก ํธ๋ฆฌํฉ๋๋ค.
|
|
|
|
```python
|
|
from transformers.testing_utils import CaptureStdout
|
|
|
|
with CaptureStdout() as cs:
|
|
function_that_writes_to_stdout()
|
|
print(cs.out)
|
|
```
|
|
|
|
๋ค์์ ์ ์ฒด ํ
์คํธ ์์ ์
๋๋ค.
|
|
|
|
```python
|
|
from transformers.testing_utils import CaptureStdout
|
|
|
|
msg = "Secret message\r"
|
|
final = "Hello World"
|
|
with CaptureStdout() as cs:
|
|
print(msg + final)
|
|
assert cs.out == final + "\n", f"captured: {cs.out}, expecting {final}"
|
|
```
|
|
|
|
`stderr`๋ฅผ ์บก์ฒํ๊ณ ์ถ๋ค๋ฉด, ๋์ `CaptureStderr` ํด๋์ค๋ฅผ ์ฌ์ฉํ์ธ์.
|
|
|
|
```python
|
|
from transformers.testing_utils import CaptureStderr
|
|
|
|
with CaptureStderr() as cs:
|
|
function_that_writes_to_stderr()
|
|
print(cs.err)
|
|
```
|
|
|
|
๋ ์คํธ๋ฆผ์ ๋์์ ์บก์ฒํด์ผ ํ๋ค๋ฉด, ๋ถ๋ชจ `CaptureStd` ํด๋์ค๋ฅผ ์ฌ์ฉํ์ธ์.
|
|
|
|
```python
|
|
from transformers.testing_utils import CaptureStd
|
|
|
|
with CaptureStd() as cs:
|
|
function_that_writes_to_stdout_and_stderr()
|
|
print(cs.err, cs.out)
|
|
```
|
|
|
|
๋ํ, ํ
์คํธ์ ๋๋ฒ๊น
์ ์ง์ํ๊ธฐ ์ํด
|
|
์ด๋ฌํ ์ปจํ
์คํธ ๊ด๋ฆฌ์๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ปจํ
์คํธ์์ ์ข
๋ฃํ ๋ ์บก์ฒ๋ ์คํธ๋ฆผ์ ์๋์ผ๋ก ๋ค์ ์คํํฉ๋๋ค.
|
|
|
|
|
|
### ๋ก๊ฑฐ ์คํธ๋ฆผ ์บก์ฒ[[capturing-logger-stream]]
|
|
|
|
๋ก๊ฑฐ ์ถ๋ ฅ์ ๊ฒ์ฆํด์ผ ํ๋ ๊ฒฝ์ฐ `CaptureLogger`๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
|
|
|
|
```python
|
|
from transformers import logging
|
|
from transformers.testing_utils import CaptureLogger
|
|
|
|
msg = "Testing 1, 2, 3"
|
|
logging.set_verbosity_info()
|
|
logger = logging.get_logger("transformers.models.bart.tokenization_bart")
|
|
with CaptureLogger(logger) as cl:
|
|
logger.info(msg)
|
|
assert cl.out, msg + "\n"
|
|
```
|
|
|
|
### ํ๊ฒฝ ๋ณ์๋ฅผ ์ด์ฉํ์ฌ ํ
์คํธ[[testing-with-environment-variables]]
|
|
|
|
ํน์ ํ
์คํธ์ ํ๊ฒฝ ๋ณ์ ์ํฅ์ ๊ฒ์ฆํ๋ ค๋ฉด
|
|
`transformers.testing_utils.mockenv`๋ผ๋ ๋์ฐ๋ฏธ ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
|
|
|
|
```python
|
|
from transformers.testing_utils import mockenv
|
|
|
|
|
|
class HfArgumentParserTest(unittest.TestCase):
|
|
@mockenv(TRANSFORMERS_VERBOSITY="error")
|
|
def test_env_override(self):
|
|
env_level_str = os.getenv("TRANSFORMERS_VERBOSITY", None)
|
|
```
|
|
|
|
์ผ๋ถ ๊ฒฝ์ฐ์๋ ์ธ๋ถ ํ๋ก๊ทธ๋จ์ ํธ์ถํด์ผํ ์๋ ์๋๋ฐ, ์ด ๋์๋ ์ฌ๋ฌ ๊ฐ์ ๋ก์ปฌ ๊ฒฝ๋ก๋ฅผ ํฌํจํ๋ `os.environ`์์ `PYTHONPATH`์ ์ค์ ์ด ํ์ํฉ๋๋ค.
|
|
ํฌํผ ํด๋์ค `transformers.test_utils.TestCasePlus`๊ฐ ๋์์ด ๋ฉ๋๋ค:
|
|
|
|
```python
|
|
from transformers.testing_utils import TestCasePlus
|
|
|
|
|
|
class EnvExampleTest(TestCasePlus):
|
|
def test_external_prog(self):
|
|
env = self.get_env()
|
|
# ์ด์ `env`๋ฅผ ์ฌ์ฉํ์ฌ ์ธ๋ถ ํ๋ก๊ทธ๋จ ํธ์ถ
|
|
```
|
|
|
|
ํ
์คํธ ํ์ผ์ด `tests` ํ
์คํธ ์ค์ํธ ๋๋ `examples`์ ์๋์ง์ ๋ฐ๋ผ
|
|
`env[PYTHONPATH]`๊ฐ ๋ ๋๋ ํฐ๋ฆฌ ์ค ํ๋๋ฅผ ํฌํจํ๋๋ก ์ค์ ๋๋ฉฐ,
|
|
ํ์ฌ ์ ์ฅ์์ ๋ํด ํ
์คํธ๊ฐ ์ํ๋๋๋ก `src` ๋๋ ํฐ๋ฆฌ๋ ํฌํจ๋ฉ๋๋ค.
|
|
ํ
์คํธ ํธ์ถ ์ด์ ์ ์ค์ ๋ ๊ฒฝ์ฐ์๋ `env[PYTHONPATH]`๋ฅผ ๊ทธ๋๋ก ์ฌ์ฉํฉ๋๋ค.
|
|
|
|
์ด ํฌํผ ๋ฉ์๋๋ `os.environ` ๊ฐ์ฒด์ ์ฌ๋ณธ์ ์์ฑํ๋ฏ๋ก ์๋ณธ์ ๊ทธ๋๋ก ์ ์ง๋ฉ๋๋ค.
|
|
|
|
|
|
### ์ฌํ ๊ฐ๋ฅํ ๊ฒฐ๊ณผ ์ป๊ธฐ[[getting-reproducible-results]]
|
|
|
|
์ผ๋ถ ์ํฉ์์ ํ
์คํธ์์ ์์์ฑ์ ์ ๊ฑฐํ์ฌ ๋์ผํ๊ฒ ์ฌํ ๊ฐ๋ฅํ ๊ฒฐ๊ณผ๋ฅผ ์ป๊ณ ์ถ์ ์ ์์ต๋๋ค.
|
|
์ด๋ฅผ ์ํด์๋ ๋ค์๊ณผ ๊ฐ์ด ์๋๋ฅผ ๊ณ ์ ํด์ผ ํฉ๋๋ค.
|
|
|
|
```python
|
|
seed = 42
|
|
|
|
# ํ์ด์ฌ RNG
|
|
import random
|
|
|
|
random.seed(seed)
|
|
|
|
# ํ์ดํ ์น RNG
|
|
import torch
|
|
|
|
torch.manual_seed(seed)
|
|
torch.backends.cudnn.deterministic = True
|
|
if torch.cuda.is_available():
|
|
torch.cuda.manual_seed_all(seed)
|
|
|
|
# ๋ํ์ด RNG
|
|
import numpy as np
|
|
|
|
np.random.seed(seed)
|
|
|
|
# ํ
์ํ๋ก RNG
|
|
tf.random.set_seed(seed)
|
|
```
|
|
|
|
### ํ
์คํธ ๋๋ฒ๊น
[[debugging tests]]
|
|
|
|
๊ฒฝ๊ณ ๊ฐ ์๋ ๊ณณ์์ ๋๋ฒ๊ฑฐ๋ฅผ ์์ํ๋ ค๋ฉด ๋ค์์ ์ํํ์ธ์.
|
|
|
|
```bash
|
|
pytest tests/utils/test_logging.py -W error::UserWarning --pdb
|
|
```
|
|
|
|
## Github Actions ์ํฌํ๋ก์ฐ ์์
์ฒ๋ฆฌ[[working-with-github-actions-workflows]]
|
|
|
|
์
ํ ํธ์ ์ํฌํ๋ก์ฐ CI ์์
์ ํธ๋ฆฌ๊ฑฐํ๋ ค๋ฉด, ๋ค์์ ์ํํด์ผ ํฉ๋๋ค.
|
|
|
|
1. `transformers` ์๋ณธ์์ ์ ๋ธ๋์น๋ฅผ ๋ง๋ญ๋๋ค(ํฌํฌ๊ฐ ์๋๋๋ค!).
|
|
2. ๋ธ๋์น ์ด๋ฆ์ `ci_` ๋๋ `ci-`๋ก ์์ํด์ผ ํฉ๋๋ค(`main`๋ ํธ๋ฆฌ๊ฑฐํ์ง๋ง `main`์์๋ PR์ ํ ์ ์์ต๋๋ค).
|
|
๋ํ ํน์ ๊ฒฝ๋ก์ ๋ํด์๋ง ํธ๋ฆฌ๊ฑฐ๋๋ฏ๋ก ์ด ๋ฌธ์๊ฐ ์์ฑ๋ ํ์ ๋ณ๊ฒฝ๋ ๋ด์ฉ์
|
|
[์ฌ๊ธฐ](https://github.com/huggingface/transformers/blob/main/.github/workflows/self-push.yml)์ *push:*์์ ํ์ธํ ์ ์์ต๋๋ค.
|
|
3. ์ด ๋ธ๋์น์์ PR์ ์์ฑํฉ๋๋ค
|
|
4. ๊ทธ๋ฐ ๋ค์ [์ฌ๊ธฐ](https://github.com/huggingface/transformers/actions/workflows/self-push.yml)์์ ์์
์ด ๋ํ๋๋์ง ํ์ธํ ์ ์์ต๋๋ค.
|
|
๋ฐฑ๋ก๊ทธ๊ฐ ์๋ ๊ฒฝ์ฐ, ๋ฐ๋ก ์คํ๋์ง ์์ ์๋ ์์ต๋๋ค.
|
|
|
|
|
|
|
|
|
|
## ์คํ์ ์ธ CI ๊ธฐ๋ฅ ํ
์คํธ[[testing-Experimental-CI-Features]]
|
|
|
|
CI ๊ธฐ๋ฅ์ ํ
์คํธํ๋ ๊ฒ์ ์ผ๋ฐ CI ์๋์ ๋ฐฉํด๊ฐ ๋ ์ ์๊ธฐ ๋๋ฌธ์ ์ ์ฌ์ ์ผ๋ก ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค.
|
|
๋ฐ๋ผ์ ์๋ก์ด CI ๊ธฐ๋ฅ์ ์ถ๊ฐํ๋ ๊ฒฝ์ฐ ๋ค์๊ณผ ๊ฐ์ด ์ํํด์ผ ํฉ๋๋ค.
|
|
|
|
1. ํ
์คํธํด์ผ ํ ๋ด์ฉ์ ํ
์คํธํ๋ ์๋ก์ด ์ ์ฉ ์์
์ ์์ฑํฉ๋๋ค.
|
|
2. ์๋ก์ด ์์
์ ํญ์ ์ฑ๊ณตํด์ผ๋ง ๋
น์ โ๋ฅผ ๋ฐ์ ์ ์์ต๋๋ค(์๋์ ์์ธํ ๋ด์ฉ์ด ์์ต๋๋ค).
|
|
3. ๋ค์ํ PR ์ ํ์ ๋ํ ํ์ธ์ ์ํด
|
|
(์ฌ์ฉ์ ํฌํฌ ๋ธ๋์น, ํฌํฌ๋์ง ์์ ๋ธ๋์น, github.com UI ์ง์ ํ์ผ ํธ์ง์์ ์์ฑ๋ ๋ธ๋์น, ๊ฐ์ ํธ์ ๋ฑ PR์ ์ ํ์ ์์ฃผ ๋ค์ํฉ๋๋ค.)
|
|
๋ฉฐ์น ๋์ ์คํ ์์
์ ๋ก๊ทธ๋ฅผ ๋ชจ๋ํฐ๋งํ๋ฉด์ ์คํํด๋ด
๋๋ค.
|
|
(์๋์ ์ผ๋ก ํญ์ ๋
น์์ ํ์ํ๋ฏ๋ก ์์
์ ์ฒด๊ฐ ๋
น์์ ์๋๋ผ๋ ์ ์ ์ ์ํฉ๋๋ค.)
|
|
4. ๋ชจ๋ ๊ฒ์ด ์์ ์ ์ธ์ง ํ์ธํ ํ, ์๋ก์ด ๋ณ๊ฒฝ ์ฌํญ์ ๊ธฐ์กด ์์
์ ๋ณํฉํฉ๋๋ค.
|
|
|
|
์ด๋ ๊ฒ ํ๋ฉด CI ๊ธฐ๋ฅ ์์ฒด์ ๋ํ ์คํ์ด ์ผ๋ฐ ์์
ํ๋ฆ์ ๋ฐฉํด๊ฐ ๋์ง ์์ต๋๋ค.
|
|
|
|
๊ทธ๋ฌ๋ ์๋ก์ด CI ๊ธฐ๋ฅ์ด ๊ฐ๋ฐ ์ค์ธ ๋์, ํญ์ ์ฑ๊ณตํ๋๋ก ํ ์ ์๋ ๋ฐฉ๋ฒ์ ๋ฌด์์ผ๊น์?
|
|
|
|
TravisCI์ ๊ฐ์ ์ผ๋ถ CI๋ `ignore-step-failure`๋ฅผ ์ง์ํ๋ฉฐ ์ ์ฒด ์์
์ ์ฑ๊ณตํ ๊ฒ์ผ๋ก ๋ณด๊ณ ํ์ง๋ง,
|
|
ํ์ฌ ์ฐ๋ฆฌ๊ฐ ์ฌ์ฉํ๋ CircleCI์ Github Actions๋ ์ด๋ฅผ ์ง์ํ์ง ์์ต๋๋ค.
|
|
|
|
๋ฐ๋ผ์ ๋ค์๊ณผ ๊ฐ์ ํด๊ฒฐ์ฑ
์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
|
|
|
|
1. bash ์คํฌ๋ฆฝํธ์์ ๊ฐ๋ฅํ ๋ง์ ์ค๋ฅ๋ฅผ ์ต์ ํ๊ธฐ ์ํด ์คํ ๋ช
๋ น์ ์์ ๋ถ๋ถ์ `set +euo pipefail`์ ์ถ๊ฐํฉ๋๋ค.
|
|
2. ๋ง์ง๋ง ๋ช
๋ น์ ๋ฐ๋์ ์ฑ๊ณตํด์ผ ํฉ๋๋ค. `echo "done"` ๋๋ `true`๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฉ๋๋ค.
|
|
|
|
์์๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
|
|
|
|
```yaml
|
|
- run:
|
|
name: run CI experiment
|
|
command: |
|
|
set +euo pipefail
|
|
echo "setting run-all-despite-any-errors-mode"
|
|
this_command_will_fail
|
|
echo "but bash continues to run"
|
|
# emulate another failure
|
|
false
|
|
# but the last command must be a success
|
|
echo "during experiment do not remove: reporting success to CI, even if there were failures"
|
|
```
|
|
|
|
๊ฐ๋จํ ๋ช
๋ น์ ๊ฒฝ์ฐ ๋ค์๊ณผ ๊ฐ์ด ์ํํ ์๋ ์์ต๋๋ค.
|
|
|
|
```bash
|
|
cmd_that_may_fail || true
|
|
```
|
|
|
|
๊ฒฐ๊ณผ์ ๋ง์กฑํ ํ์๋ ๋ฌผ๋ก , ์คํ์ ์ธ ๋จ๊ณ ๋๋ ์์
์ ์ผ๋ฐ ์์
์ ๋๋จธ์ง ๋ถ๋ถ๊ณผ ํตํฉํ๋ฉด์
|
|
`set +euo pipefail` ๋๋ ๊ธฐํ ์ถ๊ฐํ ์์๋ฅผ ์ ๊ฑฐํ์ฌ
|
|
์คํ ์์
์ด ์ผ๋ฐ CI ์๋์ ๋ฐฉํด๋์ง ์๋๋ก ํด์ผ ํฉ๋๋ค.
|
|
|
|
์ด ์ ๋ฐ์ ์ธ ๊ณผ์ ์ ์คํ ๋จ๊ณ๊ฐ PR์ ์ ๋ฐ์ ์ธ ์ํ์ ์ํฅ์ ์ฃผ์ง ์๊ณ ์คํจํ๋๋ก
|
|
`allow-failure`์ ๊ฐ์ ๊ธฐ๋ฅ์ ์ค์ ํ ์ ์๋ค๋ฉด ํจ์ฌ ๋ ์ฌ์ ์ ๊ฒ์
๋๋ค.
|
|
๊ทธ๋ฌ๋ ์์์ ์ธ๊ธํ ๋ฐ์ ๊ฐ์ด CircleCI์ Github Actions๋ ํ์ฌ ์ด๋ฌํ ๊ธฐ๋ฅ๋ค ์ง์ํ์ง ์์ต๋๋ค.
|
|
|
|
์ด ๊ธฐ๋ฅ์ ์ง์์ ์ํ ํฌํ์ ์ฐธ์ฌํ๊ณ CI ๊ด๋ จ ์ค๋ ๋๋ค์์ ์ด๋ฌํ ์ํฉ์ ํ์ธํ ์๋ ์์ต๋๋ค.
|
|
|
|
- [Github Actions:](https://github.com/actions/toolkit/issues/399)
|
|
- [CircleCI:](https://ideas.circleci.com/ideas/CCI-I-344)
|