Skip to content

Commit 4640bfb

Browse files
committed
Improve loader tests to handle various scenarios
1 parent c0710d7 commit 4640bfb

3 files changed

Lines changed: 76 additions & 32 deletions

File tree

tests/_loaders/test_csv_loader.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
# pylint: disable=not-context-manager
22
from unittest import TestCase
3-
43
from _loaders.file_test import LoaderTestBase
54
from pystreamapi.loaders import csv
65

@@ -16,13 +15,19 @@ def setUp(self):
1615

1716
def _assert_typed_rows(self, data):
1817
"""Assert that the two expected rows are present with correct types and values."""
19-
first = next(data)
18+
try:
19+
first = next(data)
20+
except StopIteration:
21+
self.fail("Expected first row but iterator was empty")
2022
self.assertEqual(first.attr1, 1)
2123
self.assertIsInstance(first.attr1, int)
2224
self.assertEqual(first.attr2, 2.0)
2325
self.assertIsInstance(first.attr2, float)
2426

25-
second = next(data)
27+
try:
28+
second = next(data)
29+
except StopIteration:
30+
self.fail("Expected second row but iterator was exhausted after first row")
2631
self.assertEqual(second.attr1, 'a')
2732
self.assertIsInstance(second.attr1, str)
2833
self.assertEqual(second.attr2, 'b')
@@ -38,7 +43,10 @@ def test_csv_loader_basic_functionality(self):
3843
def test_csv_loader_without_type_casting(self):
3944
"""Test CSV loading with type casting disabled."""
4045
with self.mock_file(self.file_content):
41-
first = next(csv(self.file_path, cast_types=False))
46+
try:
47+
first = next(csv(self.file_path, cast_types=False))
48+
except StopIteration:
49+
self.fail("Expected first row but iterator was empty")
4250
self.assertEqual(first.attr1, '1')
4351
self.assertIsInstance(first.attr1, str)
4452
self.assertEqual(first.attr2, '2.0')
@@ -53,7 +61,10 @@ def test_csv_loader_custom_delimiter(self):
5361
"""Test CSV loading with a custom delimiter."""
5462
content_with_semicolon = self.file_content.replace(",", ";")
5563
with self.mock_file(content_with_semicolon):
56-
first = next(csv(self.file_path, delimiter=';'))
64+
try:
65+
first = next(csv(self.file_path, delimiter=';'))
66+
except StopIteration:
67+
self.fail("Expected first row but iterator was empty")
5768
self.assertEqual(first.attr1, 1)
5869
self.assertEqual(first.attr2, 2.0)
5970

tests/_loaders/test_json_loader.py

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,25 +20,30 @@
2020
}
2121
]
2222
"""
23+
24+
single_object_content = """
25+
{
26+
"attr1": 1,
27+
"attr2": 2.0
28+
}
29+
"""
30+
2331
file_path = 'path/to/data.json'
2432

2533

2634
class TestJsonLoader(LoaderTestBase, TestCase):
2735

2836
def test_json_loader_from_file(self):
2937
with self.mock_file(file_content):
30-
data = json(file_path)
31-
self._check_extracted_data(data)
38+
self._check_extracted_data(json(file_path))
3239

3340
def test_json_loader_is_iterable(self):
3441
with self.mock_file(file_content):
35-
data = json(file_path)
36-
self.assertEqual(len(list(iter(data))), 2)
42+
self.assertEqual(len(list(iter(json(file_path)))), 2)
3743

3844
def test_json_loader_with_empty_file(self):
3945
with self.mock_file(""):
40-
data = json(file_path)
41-
self.assertRaises(StopIteration, next, data)
46+
self.assertRaises(StopIteration, next, json(file_path))
4247

4348
def test_json_loader_with_invalid_path(self):
4449
with self.assertRaises(FileNotFoundError):
@@ -49,30 +54,49 @@ def test_json_loader_with_no_file(self):
4954
json('../')
5055

5156
def test_json_loader_from_string(self):
52-
data = json(file_content, read_from_src=True)
53-
self._check_extracted_data(data)
57+
self._check_extracted_data(json(file_content, read_from_src=True))
5458

5559
def test_json_loader_from_empty_string(self):
5660
self.assertRaises(StopIteration, next, json("", read_from_src=True))
5761

62+
def test_json_loader_single_object_from_file(self):
63+
"""Test that a single JSON object (not array) is yielded as one namedtuple."""
64+
with self.mock_file(single_object_content):
65+
data = json(file_path)
66+
try:
67+
first = next(data)
68+
except StopIteration:
69+
self.fail("Expected one row but iterator was empty")
70+
self.assertEqual(first.attr1, 1)
71+
self.assertEqual(first.attr2, 2.0)
72+
self.assertRaises(StopIteration, next, data)
73+
74+
def test_json_loader_single_object_from_string(self):
75+
"""Test that a single JSON object string is yielded as one namedtuple."""
76+
data = json(single_object_content, read_from_src=True)
77+
try:
78+
first = next(data)
79+
except StopIteration:
80+
self.fail("Expected one row but iterator was empty")
81+
self.assertEqual(first.attr1, 1)
82+
self.assertEqual(first.attr2, 2.0)
83+
self.assertRaises(StopIteration, next, data)
84+
5885
def _check_extracted_data(self, data):
59-
# Test first row
6086
try:
6187
first = next(data)
6288
except StopIteration:
63-
return
89+
self.fail("Expected first row but iterator was empty")
6490
self.assertEqual(first.attr1, 1)
6591
self.assertIsInstance(first.attr1, int)
6692
self.assertEqual(first.attr2, 2.0)
6793
self.assertIsInstance(first.attr2, float)
6894

69-
# Test second row
7095
try:
7196
second = next(data)
7297
except StopIteration:
73-
return
98+
self.fail("Expected second row but iterator was exhausted after first row")
7499
self.assertEqual(second.attr1[0].attr1, 'a')
75100
self.assertIsInstance(second.attr1, list)
76101

77-
# Verify end of file
78102
self.assertRaises(StopIteration, next, data)

tests/_loaders/test_yaml_loader.py

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,15 @@ class TestYamlLoader(LoaderTestBase, TestCase):
2222

2323
def test_yaml_loader_from_file(self):
2424
with self.mock_file(file_content):
25-
data = yaml(file_path)
26-
self._check_extracted_data(data)
25+
self._check_extracted_data(yaml(file_path))
2726

2827
def test_yaml_loader_is_iterable(self):
2928
with self.mock_file(file_content):
30-
data = yaml(file_path)
31-
self.assertEqual(len(list(iter(data))), 2)
29+
self.assertEqual(len(list(iter(yaml(file_path)))), 2)
3230

3331
def test_yaml_loader_with_empty_file(self):
3432
with self.mock_file(""):
35-
data = yaml(file_path)
36-
self.assertEqual(len(list(data)), 0)
33+
self.assertEqual(len(list(yaml(file_path))), 0)
3734

3835
def test_yaml_loader_with_invalid_path(self):
3936
with self.assertRaises(FileNotFoundError):
@@ -44,35 +41,47 @@ def test_yaml_loader_with_no_file(self):
4441
yaml('../')
4542

4643
def test_yaml_loader_from_string(self):
47-
data = yaml(file_content, read_from_src=True)
48-
self._check_extracted_data(data)
44+
self._check_extracted_data(yaml(file_content, read_from_src=True))
4945

5046
def test_yaml_loader_from_empty_string(self):
5147
self.assertEqual(list(yaml('', read_from_src=True)), [])
5248

5349
def test_yaml_loader_is_lazy(self):
5450
with self.mock_file(file_content):
55-
data = yaml(file_path)
56-
self.assertIsInstance(data, GeneratorType)
51+
self.assertIsInstance(yaml(file_path), GeneratorType)
5752

5853
def test_yaml_loader_with_malformed_yaml(self):
59-
malformed_yaml = "key: : invalid"
6054
with self.assertRaises(yaml_lib.YAMLError):
61-
list(yaml(malformed_yaml, read_from_src=True))
55+
list(yaml("key: : invalid", read_from_src=True))
56+
57+
def test_yaml_loader_skips_null_document(self):
58+
"""Test that a null/~ document (falsy) is silently skipped."""
59+
with self.mock_file("~"):
60+
self.assertEqual(list(yaml(file_path)), [])
61+
self.assertEqual(list(yaml("~", read_from_src=True)), [])
62+
63+
def test_yaml_loader_skips_empty_document_in_stream(self):
64+
"""Test that an empty document in a multi-document stream is silently skipped."""
65+
content = "---\n- attr1: 1\n---\n" # second document is empty
66+
with self.mock_file(content):
67+
self.assertEqual(len(list(yaml(file_path))), 1)
68+
self.assertEqual(len(list(yaml(content, read_from_src=True))), 1)
6269

6370
def _check_extracted_data(self, data):
6471
try:
6572
first = next(data)
6673
except StopIteration:
67-
return
74+
self.fail("Expected first row but iterator was empty")
6875
self.assertEqual(first.attr1, 1)
6976
self.assertIsInstance(first.attr1, int)
7077
self.assertEqual(first.attr2, 2.0)
7178
self.assertIsInstance(first.attr2, float)
79+
7280
try:
7381
second = next(data)
7482
except StopIteration:
75-
return
83+
self.fail("Expected second row but iterator was exhausted after first row")
7684
self.assertIsInstance(second.attr1, list)
7785
self.assertEqual(second.attr1[0].attr1, 'a')
86+
7887
self.assertRaises(StopIteration, next, data)

0 commit comments

Comments
 (0)