Skip to content

Commit 57f5308

Browse files
committed
streamline get_selector_values() and add tests to it
1 parent 42296f2 commit 57f5308

File tree

2 files changed

+61
-4
lines changed

2 files changed

+61
-4
lines changed

itemloaders/__init__.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -422,13 +422,11 @@ def get_css(self, css, *processors, **kw):
422422
values = self.get_selector_values(None, css, 'css', **kw)
423423
return self.get_value(values, *processors, **kw)
424424

425-
def get_selector_values(self, field_name, selector_rules, selector_name, **kw):
426-
427-
selector = getattr(self.selector, selector_name, None)
425+
def get_selector_values(self, field_name, selector_rules, selector_type, **kw):
428426

429427
self._check_selector_method()
430428

431-
selector_type = selector.__name__ # either 'css' or 'xpath'
429+
selector = getattr(self.selector, selector_type or '', None)
432430

433431
# The optional arg in methods like `add_css()` for context in stats
434432
name = kw.get("name")

tests/test_selector_loader.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import unittest
2+
import pytest
3+
from unittest import mock
24

35
from parsel import Selector
6+
from parsel.utils import flatten
47

58
from itemloaders import ItemLoader
69
from itemloaders.processors import MapCompose, TakeFirst
@@ -151,3 +154,59 @@ def test_replace_css_re(self):
151154
self.assertEqual(loader.get_output_value('url'), ['http://www.scrapy.org'])
152155
loader.replace_css('url', 'a::attr(href)', re=r'http://www\.(.+)')
153156
self.assertEqual(loader.get_output_value('url'), ['scrapy.org'])
157+
158+
159+
def test_get_selector_values_with_no_selector():
160+
"""It should raise an error if it's not configured with any Selector."""
161+
162+
loader = ItemLoader()
163+
164+
with pytest.raises(RuntimeError) as err:
165+
loader.get_selector_values("field_name", [], None)
166+
167+
168+
def test_get_selector_values():
169+
"""Selectors must be properly called as well as correctly flatten the data.
170+
171+
For this test, we're testing 'css', but it should also work the same for 'xpath'.
172+
"""
173+
174+
selector_rules = ["#rule1", "#rule2", "#rule3"]
175+
field_name = "field"
176+
parsed_data = ["data1", "data2"]
177+
178+
mock_css_selector = mock.Mock()
179+
mock_css_selector().getall.return_value = parsed_data
180+
mock_css_selector.__name__ = "css"
181+
182+
mock_selector = mock.Mock()
183+
mock_selector.css = mock_css_selector
184+
185+
loader = ItemLoader(selector=mock_selector)
186+
loader.write_to_stats = mock.Mock()
187+
188+
# This wasn't actually initialized so it will return 0 by default otherwise.
189+
loader.field_tracker["field_css"] = 1
190+
191+
result = loader.get_selector_values(field_name, selector_rules, "css")
192+
193+
assert result == flatten([parsed_data] * len(selector_rules))
194+
195+
mock_selector.assert_has_calls(
196+
[
197+
mock.call.css(selector_rules[0]),
198+
mock.call.css().getall(),
199+
mock.call.css(selector_rules[1]),
200+
mock.call.css().getall(),
201+
mock.call.css(selector_rules[2]),
202+
mock.call.css().getall(),
203+
]
204+
)
205+
206+
loader.write_to_stats.assert_has_calls(
207+
[
208+
mock.call(field_name, parsed_data, 1, "css", name=None),
209+
mock.call(field_name, parsed_data, 2, "css", name=None),
210+
mock.call(field_name, parsed_data, 3, "css", name=None),
211+
]
212+
)

0 commit comments

Comments
 (0)