From b46e7b9ef5fe82592ecb3e88e5769bf8c6769344 Mon Sep 17 00:00:00 2001 From: Eric Joanis Date: Wed, 29 Apr 2026 10:50:48 -0400 Subject: [PATCH 1/4] refactor(tests): replace all one-line self.assertTrue(x) by assert x --- aligner/tests/test_cli.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/aligner/tests/test_cli.py b/aligner/tests/test_cli.py index e784224..b9c56ca 100644 --- a/aligner/tests/test_cli.py +++ b/aligner/tests/test_cli.py @@ -96,8 +96,8 @@ def test_align_something(self): os.system("ls -la " + tmpdir) print(result.output) self.assertEqual(result.exit_code, 0) - self.assertTrue(textgrid.exists()) - self.assertTrue(wav_out.exists()) + assert textgrid.exists() + assert wav_out.exists() with self.subTest("ctc-segmenter extract"): result = self.runner.invoke( @@ -106,11 +106,11 @@ def test_align_something(self): if result.exit_code != 0: print(result.output) self.assertEqual(result.exit_code, 0) - self.assertTrue((tmppath / "out/metadata.psv").exists()) + assert (tmppath / "out/metadata.psv").exists() with open(txt, encoding="utf8") as txt_f: non_blank_line_count = sum(1 for line in txt_f if line.strip()) for i in range(non_blank_line_count): - self.assertTrue((tmppath / f"out/wavs/segment{i}.wav")) + assert (tmppath / f"out/wavs/segment{i}.wav").exists() class MiscTests(TestCase): From 5be048d284ecf98121ee426e7200cce78d538d3a Mon Sep 17 00:00:00 2001 From: Eric Joanis Date: Wed, 29 Apr 2026 11:03:54 -0400 Subject: [PATCH 2/4] refactor(tests): replace most self.assert* by assert --- aligner/tests/test_cli.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/aligner/tests/test_cli.py b/aligner/tests/test_cli.py index b9c56ca..20e1b5d 100644 --- a/aligner/tests/test_cli.py +++ b/aligner/tests/test_cli.py @@ -29,23 +29,23 @@ def test_main_help(self): for help in "-h", "--help": with self.subTest(help=help): result = self.runner.invoke(app, [help]) - self.assertEqual(result.exit_code, 0) - self.assertIn("align", result.stdout) - self.assertIn("extract", result.stdout) + assert result.exit_code == 0 + assert "align" in result.stdout + assert "extract" in result.stdout def test_sub_help(self): for cmd in "align", "extract": for help in "-h", "--help": with self.subTest(cmd=cmd, help=help): result = self.runner.invoke(app, [cmd, help]) - self.assertEqual(result.exit_code, 0) - self.assertIn("Usage:", result.stdout) - self.assertIn(cmd, result.stdout) + assert result.exit_code == 0 + assert "Usage:" in result.stdout + assert cmd in result.stdout def test_align_empty_file(self): with self.subTest("empty file"): result = self.runner.invoke(app, ["align", os.devnull, os.devnull]) - self.assertNotEqual(result.exit_code, 0) + assert result.exit_code != 0 self.assertRegex(result.output, r"(?s)is.*empty") with self.subTest("file with only empty lines"): @@ -54,7 +54,7 @@ def test_align_empty_file(self): with open(textfile, "w", encoding="utf8") as f: f.write("\n \n \n") result = self.runner.invoke(app, ["align", textfile, os.devnull]) - self.assertNotEqual(result.exit_code, 0) + assert result.exit_code != 0 self.assertRegex(result.output, r"(?s)is.*empty") def fetch_ras_test_file(self, filename, outputdir): @@ -95,7 +95,7 @@ def test_align_something(self): if result.exit_code != 0: os.system("ls -la " + tmpdir) print(result.output) - self.assertEqual(result.exit_code, 0) + assert result.exit_code == 0 assert textgrid.exists() assert wav_out.exists() @@ -105,7 +105,7 @@ def test_align_something(self): ) if result.exit_code != 0: print(result.output) - self.assertEqual(result.exit_code, 0) + assert result.exit_code == 0 assert (tmppath / "out/metadata.psv").exists() with open(txt, encoding="utf8") as txt_f: non_blank_line_count = sum(1 for line in txt_f if line.strip()) @@ -116,5 +116,5 @@ def test_align_something(self): class MiscTests(TestCase): def test_segment(self): segment = Segment("text", 500, 700, 0.42) - self.assertEqual(len(segment), 200) - self.assertEqual(repr(segment), "text (0.42): [ 500, 700)") + assert len(segment) == 200 + assert repr(segment) == "text (0.42): [ 500, 700)" From 139c59e72561f39f630b3d14b62aa75d8cb2169e Mon Sep 17 00:00:00 2001 From: Eric Joanis Date: Fri, 1 May 2026 10:06:39 -0400 Subject: [PATCH 3/4] refactor(tests): complete the conversion to pytest --- aligner/tests/test_cli.py | 51 +++++++++++++++++++++------------------ pyproject.toml | 3 ++- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/aligner/tests/test_cli.py b/aligner/tests/test_cli.py index 20e1b5d..4a63bc9 100644 --- a/aligner/tests/test_cli.py +++ b/aligner/tests/test_cli.py @@ -5,14 +5,15 @@ import io import os +import re import subprocess import tempfile from contextlib import redirect_stderr from pathlib import Path -from unittest import SkipTest, TestCase from urllib.error import URLError from urllib.request import Request, urlopen +from pytest import fixture, skip from typer.testing import CliRunner from ..classes import Segment @@ -21,41 +22,43 @@ VERBOSE_OVERRIDE = bool(os.environ.get("EVERYVOICE_VERBOSE_TESTS", False)) -class CLITest(TestCase): - def setUp(self) -> None: - self.runner = CliRunner() +@fixture +def runner() -> CliRunner: + return CliRunner() - def test_main_help(self): + +class TestCLI: + def test_main_help(self, runner, subtests): for help in "-h", "--help": - with self.subTest(help=help): - result = self.runner.invoke(app, [help]) + with subtests.test(help=help): + result = runner.invoke(app, [help]) assert result.exit_code == 0 assert "align" in result.stdout assert "extract" in result.stdout - def test_sub_help(self): + def test_sub_help(self, runner, subtests): for cmd in "align", "extract": for help in "-h", "--help": - with self.subTest(cmd=cmd, help=help): - result = self.runner.invoke(app, [cmd, help]) + with subtests.test(cmd=cmd, help=help): + result = runner.invoke(app, [cmd, help]) assert result.exit_code == 0 assert "Usage:" in result.stdout assert cmd in result.stdout - def test_align_empty_file(self): - with self.subTest("empty file"): - result = self.runner.invoke(app, ["align", os.devnull, os.devnull]) + def test_align_empty_file(self, runner, subtests): + with subtests.test("empty file"): + result = runner.invoke(app, ["align", os.devnull, os.devnull]) assert result.exit_code != 0 - self.assertRegex(result.output, r"(?s)is.*empty") + assert re.search(r"(?s)is.*empty", result.output) - with self.subTest("file with only empty lines"): + with subtests.test("file with only empty lines"): with tempfile.TemporaryDirectory() as tmpdir: textfile = os.path.join(tmpdir, "emptylines.txt") with open(textfile, "w", encoding="utf8") as f: f.write("\n \n \n") - result = self.runner.invoke(app, ["align", textfile, os.devnull]) + result = runner.invoke(app, ["align", textfile, os.devnull]) assert result.exit_code != 0 - self.assertRegex(result.output, r"(?s)is.*empty") + assert re.search(r"(?s)is.*empty", result.output) def fetch_ras_test_file(self, filename, outputdir): repo, path = "https://github.com/ReadAlongs/Studio/", "/tests/data/" @@ -65,7 +68,7 @@ def fetch_ras_test_file(self, filename, outputdir): with open(os.path.join(outputdir, filename), "wb") as f: f.write(response.read()) - def test_align_something(self): + def test_align_something(self, runner, subtests): with tempfile.TemporaryDirectory() as tmpdir: tmppath = Path(tmpdir) try: @@ -73,7 +76,7 @@ def test_align_something(self): self.fetch_ras_test_file("ej-fra.txt", tmpdir) self.fetch_ras_test_file("ej-fra.m4a", tmpdir) except URLError as e: # pragma: no cover - raise SkipTest( + raise skip( f"Can't fetch test data: {e}; skipping the test that depends on the Internet." ) txt = tmppath / "ej-fra.txt" @@ -90,8 +93,8 @@ def test_align_something(self): textgrid = tmppath / "ej-fra-16000.TextGrid" wav_out = tmppath / "ej-fra-16000.wav" - with self.subTest("ctc-segmenter align"): - result = self.runner.invoke(app, ["align", str(txt), str(wav)]) + with subtests.test("ctc-segmenter align"): + result = runner.invoke(app, ["align", str(txt), str(wav)]) if result.exit_code != 0: os.system("ls -la " + tmpdir) print(result.output) @@ -99,8 +102,8 @@ def test_align_something(self): assert textgrid.exists() assert wav_out.exists() - with self.subTest("ctc-segmenter extract"): - result = self.runner.invoke( + with subtests.test("ctc-segmenter extract"): + result = runner.invoke( app, ["extract", str(textgrid), str(wav_out), str(tmppath / "out")] ) if result.exit_code != 0: @@ -113,7 +116,7 @@ def test_align_something(self): assert (tmppath / f"out/wavs/segment{i}.wav").exists() -class MiscTests(TestCase): +class TestMisc: def test_segment(self): segment = Segment("text", 500, 700, 0.42) assert len(segment) == 200 diff --git a/pyproject.toml b/pyproject.toml index a1094f1..825e711 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,13 +45,14 @@ include = ["/aligner"] [project.optional-dependencies] dev = [ "black~=24.3", + "coverage", "flake8>=4.0.1", "gitlint-core>=0.19.0", "isort>=5.10.1", "mypy>=1.8.0", "pre-commit>=3.2.0", "pytest", - "coverage", + "pytest-subtests", ] [project.urls] From 2618e6573fc476753fe6af3b6c0bd67610bf5984 Mon Sep 17 00:00:00 2001 From: Eric Joanis Date: Fri, 1 May 2026 10:12:28 -0400 Subject: [PATCH 4/4] refactor(tests): put the runner fixture at the class level --- aligner/tests/test_cli.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/aligner/tests/test_cli.py b/aligner/tests/test_cli.py index 4a63bc9..d2c69cd 100644 --- a/aligner/tests/test_cli.py +++ b/aligner/tests/test_cli.py @@ -13,7 +13,7 @@ from urllib.error import URLError from urllib.request import Request, urlopen -from pytest import fixture, skip +import pytest from typer.testing import CliRunner from ..classes import Segment @@ -22,32 +22,33 @@ VERBOSE_OVERRIDE = bool(os.environ.get("EVERYVOICE_VERBOSE_TESTS", False)) -@fixture -def runner() -> CliRunner: - return CliRunner() +@pytest.fixture(scope="class") +def runner(request) -> None: + request.cls.runner = CliRunner() +@pytest.mark.usefixtures("runner") class TestCLI: - def test_main_help(self, runner, subtests): + def test_main_help(self, subtests): for help in "-h", "--help": with subtests.test(help=help): - result = runner.invoke(app, [help]) + result = self.runner.invoke(app, [help]) assert result.exit_code == 0 assert "align" in result.stdout assert "extract" in result.stdout - def test_sub_help(self, runner, subtests): + def test_sub_help(self, subtests): for cmd in "align", "extract": for help in "-h", "--help": with subtests.test(cmd=cmd, help=help): - result = runner.invoke(app, [cmd, help]) + result = self.runner.invoke(app, [cmd, help]) assert result.exit_code == 0 assert "Usage:" in result.stdout assert cmd in result.stdout - def test_align_empty_file(self, runner, subtests): + def test_align_empty_file(self, subtests): with subtests.test("empty file"): - result = runner.invoke(app, ["align", os.devnull, os.devnull]) + result = self.runner.invoke(app, ["align", os.devnull, os.devnull]) assert result.exit_code != 0 assert re.search(r"(?s)is.*empty", result.output) @@ -56,7 +57,7 @@ def test_align_empty_file(self, runner, subtests): textfile = os.path.join(tmpdir, "emptylines.txt") with open(textfile, "w", encoding="utf8") as f: f.write("\n \n \n") - result = runner.invoke(app, ["align", textfile, os.devnull]) + result = self.runner.invoke(app, ["align", textfile, os.devnull]) assert result.exit_code != 0 assert re.search(r"(?s)is.*empty", result.output) @@ -68,7 +69,7 @@ def fetch_ras_test_file(self, filename, outputdir): with open(os.path.join(outputdir, filename), "wb") as f: f.write(response.read()) - def test_align_something(self, runner, subtests): + def test_align_something(self, subtests): with tempfile.TemporaryDirectory() as tmpdir: tmppath = Path(tmpdir) try: @@ -76,7 +77,7 @@ def test_align_something(self, runner, subtests): self.fetch_ras_test_file("ej-fra.txt", tmpdir) self.fetch_ras_test_file("ej-fra.m4a", tmpdir) except URLError as e: # pragma: no cover - raise skip( + raise pytest.skip( f"Can't fetch test data: {e}; skipping the test that depends on the Internet." ) txt = tmppath / "ej-fra.txt" @@ -94,7 +95,7 @@ def test_align_something(self, runner, subtests): wav_out = tmppath / "ej-fra-16000.wav" with subtests.test("ctc-segmenter align"): - result = runner.invoke(app, ["align", str(txt), str(wav)]) + result = self.runner.invoke(app, ["align", str(txt), str(wav)]) if result.exit_code != 0: os.system("ls -la " + tmpdir) print(result.output) @@ -103,7 +104,7 @@ def test_align_something(self, runner, subtests): assert wav_out.exists() with subtests.test("ctc-segmenter extract"): - result = runner.invoke( + result = self.runner.invoke( app, ["extract", str(textgrid), str(wav_out), str(tmppath / "out")] ) if result.exit_code != 0: