mirror of https://github.com/scrapy/scrapy.git
Improve test coverage for contracts (#7677)
This commit is contained in:
parent
0007676e8d
commit
0ccddb4f61
|
|
@ -133,6 +133,29 @@ class DemoSpider(Spider):
|
||||||
"""
|
"""
|
||||||
return DemoItem(url=response.url)
|
return DemoItem(url=response.url)
|
||||||
|
|
||||||
|
def returns_request_range_fail(self, response):
|
||||||
|
"""method which returns fewer requests than the expected range
|
||||||
|
@url http://scrapy.org
|
||||||
|
@returns requests 2 3
|
||||||
|
"""
|
||||||
|
return Request("http://scrapy.org", callback=self.returns_item)
|
||||||
|
|
||||||
|
def yields_item_and_request(self, response):
|
||||||
|
"""yields one item and one request
|
||||||
|
@url http://scrapy.org
|
||||||
|
@returns items 1 1
|
||||||
|
@scrapes name url
|
||||||
|
"""
|
||||||
|
yield DemoItem(name="test", url=response.url)
|
||||||
|
yield Request("http://scrapy.org", callback=self.returns_item)
|
||||||
|
|
||||||
|
async def returns_async_gen(self, response):
|
||||||
|
"""async generator callback
|
||||||
|
@url http://scrapy.org
|
||||||
|
@returns items 1 1
|
||||||
|
"""
|
||||||
|
yield DemoItem(url=response.url)
|
||||||
|
|
||||||
def returns_dict_fail(self, response):
|
def returns_dict_fail(self, response):
|
||||||
"""method which returns item
|
"""method which returns item
|
||||||
@url http://scrapy.org
|
@url http://scrapy.org
|
||||||
|
|
@ -437,6 +460,48 @@ class TestContractsManager:
|
||||||
request.callback(response)
|
request.callback(response)
|
||||||
self.should_error()
|
self.should_error()
|
||||||
|
|
||||||
|
def test_returns_invalid_argument_count(self):
|
||||||
|
spider = DemoSpider()
|
||||||
|
with pytest.raises(ValueError, match="expected 1, 2 or 3, got 0"):
|
||||||
|
ReturnsContract(spider.returns_item)
|
||||||
|
with pytest.raises(ValueError, match="expected 1, 2 or 3, got 4"):
|
||||||
|
ReturnsContract(spider.returns_item, "items", "1", "2", "3")
|
||||||
|
|
||||||
|
def test_returns_default_bounds(self):
|
||||||
|
spider = DemoSpider()
|
||||||
|
contract = ReturnsContract(spider.returns_item, "items")
|
||||||
|
assert contract.min_bound == 1
|
||||||
|
assert contract.max_bound == float("inf")
|
||||||
|
|
||||||
|
def test_returns_range_fail(self):
|
||||||
|
spider = DemoSpider()
|
||||||
|
response = ResponseMock()
|
||||||
|
|
||||||
|
request = self.conman.from_method(
|
||||||
|
spider.returns_request_range_fail, self.results
|
||||||
|
)
|
||||||
|
request.callback(response)
|
||||||
|
self.should_fail()
|
||||||
|
assert "expected 2..3" in self.results.failures[-1][-1]
|
||||||
|
|
||||||
|
def test_returns_and_scrapes_ignore_other_types(self):
|
||||||
|
spider = DemoSpider()
|
||||||
|
response = ResponseMock()
|
||||||
|
|
||||||
|
# @returns and @scrapes only count matching output objects and skip
|
||||||
|
# the request that is also yielded.
|
||||||
|
request = self.conman.from_method(spider.yields_item_and_request, self.results)
|
||||||
|
request.callback(response)
|
||||||
|
self.should_succeed()
|
||||||
|
|
||||||
|
def test_testcase_str(self):
|
||||||
|
spider = DemoSpider()
|
||||||
|
contract = UrlContract(spider.returns_request, "http://scrapy.org")
|
||||||
|
assert (
|
||||||
|
str(contract.testcase_pre)
|
||||||
|
== "[demo_spider] returns_request (@url pre-hook)"
|
||||||
|
)
|
||||||
|
|
||||||
def test_scrapes(self):
|
def test_scrapes(self):
|
||||||
spider = DemoSpider()
|
spider = DemoSpider()
|
||||||
response = ResponseMock()
|
response = ResponseMock()
|
||||||
|
|
@ -570,6 +635,41 @@ class CustomFailContractPostProcess(Contract):
|
||||||
raise KeyboardInterrupt("Post-process exception")
|
raise KeyboardInterrupt("Post-process exception")
|
||||||
|
|
||||||
|
|
||||||
|
class PreProcessSuccessContract(Contract):
|
||||||
|
name = "pre_success"
|
||||||
|
|
||||||
|
def pre_process(self, response):
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
class PreProcessAssertionFailContract(Contract):
|
||||||
|
name = "pre_assertion_fail"
|
||||||
|
|
||||||
|
def pre_process(self, response):
|
||||||
|
raise AssertionError("pre-process assertion")
|
||||||
|
|
||||||
|
|
||||||
|
class PreProcessErrorContract(Contract):
|
||||||
|
name = "pre_error"
|
||||||
|
|
||||||
|
def pre_process(self, response):
|
||||||
|
raise ValueError("pre-process error")
|
||||||
|
|
||||||
|
|
||||||
|
class PostProcessSuccessContract(Contract):
|
||||||
|
name = "post_success"
|
||||||
|
|
||||||
|
def post_process(self, output):
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
class PostProcessErrorContract(Contract):
|
||||||
|
name = "post_error"
|
||||||
|
|
||||||
|
def post_process(self, output):
|
||||||
|
raise ValueError("post-process error")
|
||||||
|
|
||||||
|
|
||||||
class TestCustomContractPrePostProcess:
|
class TestCustomContractPrePostProcess:
|
||||||
def setup_method(self):
|
def setup_method(self):
|
||||||
self.results = TextTestResult(stream=None, descriptions=False, verbosity=0)
|
self.results = TextTestResult(stream=None, descriptions=False, verbosity=0)
|
||||||
|
|
@ -601,3 +701,83 @@ class TestCustomContractPrePostProcess:
|
||||||
|
|
||||||
assert not self.results.failures
|
assert not self.results.failures
|
||||||
assert not self.results.errors
|
assert not self.results.errors
|
||||||
|
|
||||||
|
def test_pre_hook_success(self):
|
||||||
|
spider = DemoSpider()
|
||||||
|
response = ResponseMock()
|
||||||
|
contract = PreProcessSuccessContract(spider.returns_request)
|
||||||
|
conman = ContractsManager([UrlContract, ReturnsContract, contract])
|
||||||
|
|
||||||
|
request = conman.from_method(spider.returns_request, self.results)
|
||||||
|
contract.add_pre_hook(request, self.results)
|
||||||
|
request.callback(response, **request.cb_kwargs)
|
||||||
|
|
||||||
|
assert not self.results.failures
|
||||||
|
assert not self.results.errors
|
||||||
|
|
||||||
|
def test_pre_hook_assertion_failure(self):
|
||||||
|
spider = DemoSpider()
|
||||||
|
response = ResponseMock()
|
||||||
|
contract = PreProcessAssertionFailContract(spider.returns_request)
|
||||||
|
conman = ContractsManager([UrlContract, ReturnsContract, contract])
|
||||||
|
|
||||||
|
request = conman.from_method(spider.returns_request, self.results)
|
||||||
|
contract.add_pre_hook(request, self.results)
|
||||||
|
request.callback(response, **request.cb_kwargs)
|
||||||
|
|
||||||
|
assert self.results.failures
|
||||||
|
assert not self.results.errors
|
||||||
|
|
||||||
|
def test_pre_hook_error(self):
|
||||||
|
spider = DemoSpider()
|
||||||
|
response = ResponseMock()
|
||||||
|
contract = PreProcessErrorContract(spider.returns_request)
|
||||||
|
conman = ContractsManager([UrlContract, ReturnsContract, contract])
|
||||||
|
|
||||||
|
request = conman.from_method(spider.returns_request, self.results)
|
||||||
|
contract.add_pre_hook(request, self.results)
|
||||||
|
request.callback(response, **request.cb_kwargs)
|
||||||
|
|
||||||
|
assert self.results.errors
|
||||||
|
|
||||||
|
def test_pre_hook_async_callback(self):
|
||||||
|
spider = DemoSpider()
|
||||||
|
response = ResponseMock()
|
||||||
|
contract = PreProcessSuccessContract(spider.returns_request_async)
|
||||||
|
request = Request("http://scrapy.org", callback=spider.returns_request_async)
|
||||||
|
contract.add_pre_hook(request, self.results)
|
||||||
|
|
||||||
|
with pytest.raises(TypeError, match="async callbacks"):
|
||||||
|
request.callback(response)
|
||||||
|
|
||||||
|
def test_pre_hook_async_generator(self):
|
||||||
|
spider = DemoSpider()
|
||||||
|
response = ResponseMock()
|
||||||
|
contract = PreProcessSuccessContract(spider.returns_async_gen)
|
||||||
|
request = Request("http://scrapy.org", callback=spider.returns_async_gen)
|
||||||
|
contract.add_pre_hook(request, self.results)
|
||||||
|
|
||||||
|
with pytest.raises(TypeError, match="async callbacks"):
|
||||||
|
request.callback(response)
|
||||||
|
|
||||||
|
def test_post_hook_async_generator(self):
|
||||||
|
spider = DemoSpider()
|
||||||
|
response = ResponseMock()
|
||||||
|
contract = PostProcessSuccessContract(spider.returns_async_gen)
|
||||||
|
request = Request("http://scrapy.org", callback=spider.returns_async_gen)
|
||||||
|
contract.add_post_hook(request, self.results)
|
||||||
|
|
||||||
|
with pytest.raises(TypeError, match="async callbacks"):
|
||||||
|
request.callback(response)
|
||||||
|
|
||||||
|
def test_post_hook_error(self):
|
||||||
|
spider = DemoSpider()
|
||||||
|
response = ResponseMock()
|
||||||
|
contract = PostProcessErrorContract(spider.returns_request)
|
||||||
|
conman = ContractsManager([UrlContract, ReturnsContract, contract])
|
||||||
|
|
||||||
|
request = conman.from_method(spider.returns_request, self.results)
|
||||||
|
contract.add_post_hook(request, self.results)
|
||||||
|
request.callback(response, **request.cb_kwargs)
|
||||||
|
|
||||||
|
assert self.results.errors
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue