Repository: scrapy/scrapy

Issue:
Cannot use contracts with inherited callbacks
### Description

If you want to `scrapy check` a spider that has inherited methods, these methods' contracts will be ignored.

### Reproduce

```python
class BaseSpider(Spider):

    def returns_request(self, response):
        """ method which returns request
        @url https://docs.scrapy.org/en/latest/
        @returns requests 1
        """
        return Request('http://scrapy.org', callback=self.returns_item)


class DemoSpider(BaseSpider):
    name = 'demo_spider'
```

And then run `scrapy check`.

You'll get the following output:

```
----------------------------------------------------------------------
Ran 0 contracts in 0.000s

OK
```

### Reason

`ContractsManager.tested_methods_from_spidercls` uses `vars(spidercls).items()` to get methods.

### Solution

Use `inspect.getmembers(spidercls)` instead.

Discussion:

Patch:
diff --git a/scrapy/contracts/__init__.py b/scrapy/contracts/__init__.py
index 8315d21d2..de7ac4503 100644
--- a/scrapy/contracts/__init__.py
+++ b/scrapy/contracts/__init__.py
@@ -1,6 +1,7 @@
 import sys
 import re
 from functools import wraps
+from inspect import getmembers
 from unittest import TestCase
 
 from scrapy.http import Request
@@ -17,7 +18,7 @@ class ContractsManager(object):
 
     def tested_methods_from_spidercls(self, spidercls):
         methods = []
-        for key, value in vars(spidercls).items():
+        for key, value in getmembers(spidercls):
             if (callable(value) and value.__doc__ and
                     re.search(r'^\s*@', value.__doc__, re.MULTILINE)):
                 methods.append(key)


Test patch:
diff --git a/tests/test_contracts.py b/tests/test_contracts.py
index b07cbee1e..322d20c4c 100644
--- a/tests/test_contracts.py
+++ b/tests/test_contracts.py
@@ -101,6 +101,10 @@ class TestSpider(Spider):
         pass
 
 
+class InheritsTestSpider(TestSpider):
+    name = 'inherits_demo_spider'
+
+
 class ContractsManagerTest(unittest.TestCase):
     contracts = [UrlContract, ReturnsContract, ScrapesContract]
 
@@ -202,3 +206,9 @@ class ContractsManagerTest(unittest.TestCase):
 
         self.assertFalse(self.results.failures)
         self.assertTrue(self.results.errors)
+
+    def test_inherited_contracts(self):
+        spider = InheritsTestSpider()
+
+        requests = self.conman.from_spider(spider, self.results)
+        self.assertTrue(requests)