• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

doc/H02-Mar-2020-1,035606

docker/H02-Mar-2020-8258

examples/H02-Mar-2020-7955

src/hamcrest/H02-Mar-2020-2,8882,056

tests/H02-Mar-2020-2,9662,093

.coveragercH A D02-Mar-202024 32

.gitignoreH A D02-Mar-2020104 1211

.travis.ymlH A D02-Mar-20201.1 KiB6357

MANIFEST.inH A D02-Mar-2020114 65

README.rstH A D02-Mar-202013.5 KiB355257

release.shH A D02-Mar-20201 KiB5240

setup.cfgH A D02-Mar-202054 43

setup.pyH A D02-Mar-20202.2 KiB7057

tox.iniH A D02-Mar-20202.4 KiB11096

README.rst

1PyHamcrest
2==========
3
4| |docs| |travis| |coveralls| |landscape| |scrutinizer|
5| |version| |downloads| |wheel| |supported-versions| |supported-implementations|
6| |GitHub forks| |GitHub stars| |GitHub watchers| |GitHub contributors| |Lines of Code|
7| |GitHub issues| |GitHub issues-closed| |GitHub pull-requests| |GitHub pull-requests closed|
8
9.. |docs| image:: https://readthedocs.org/projects/pyhamcrest/badge/
10    :target: https://pyhamcrest.readthedocs.org/
11    :alt: Documentation Status
12
13.. |travis| image:: http://img.shields.io/travis/hamcrest/PyHamcrest/master.svg
14    :alt: Travis-CI Build Status
15    :target: https://travis-ci.org/hamcrest/PyHamcrest
16
17.. |appveyor| image:: https://ci.appveyor.com/api/projects/status/github/hamcrest/PyHamcrest?branch=master&svg=true
18    :alt: AppVeyor Build Status
19    :target: https://ci.appveyor.com/project/hamcrest/PyHamcrest
20
21.. |coveralls| image:: http://img.shields.io/coveralls/hamcrest/PyHamcrest/master.svg?style=flat
22    :alt: Coverage Status
23    :target: https://coveralls.io/r/hamcrest/PyHamcrest
24
25.. |landscape| image:: https://landscape.io/github/hamcrest/PyHamcrest/master/landscape.svg?style=flat
26    :target: https://landscape.io/github/hamcrest/PyHamcrest/master
27    :alt: Code Quality Status
28
29.. |version| image:: http://img.shields.io/pypi/v/PyHamcrest.svg?style=flat
30    :alt: PyPI Package latest release
31    :target: https://pypi.python.org/pypi/PyHamcrest
32
33.. |downloads| image:: http://img.shields.io/pypi/dm/PyHamcrest.svg?style=flat
34    :alt: PyPI Package monthly downloads
35    :target: https://pypi.python.org/pypi/PyHamcrest
36
37.. |wheel| image:: https://pypip.in/wheel/PyHamcrest/badge.svg?style=flat
38    :alt: PyPI Wheel
39    :target: https://pypi.python.org/pypi/PyHamcrest
40
41.. |supported-versions| image:: https://pypip.in/py_versions/PyHamcrest/badge.svg?style=flat
42    :alt: Supported versions
43    :target: https://pypi.python.org/pypi/PyHamcrest
44
45.. |GitHub forks| image:: https://img.shields.io/github/forks/hamcrest/PyHamcrest.svg?label=Fork&logo=github
46    :alt: GitHub forks
47    :target: https://github.com/hamcrest/PyHamcrest/network/members
48
49.. |GitHub stars| image:: https://img.shields.io/github/stars/hamcrest/PyHamcrest.svg?label=Star&logo=github
50    :alt: GitHub stars
51    :target: https://github.com/hamcrest/PyHamcrest/stargazers/
52
53.. |GitHub watchers| image:: https://img.shields.io/github/watchers/hamcrest/PyHamcrest.svg?label=Watch&logo=github
54    :alt: GitHub watchers
55    :target: https://github.com/hamcrest/PyHamcrest/watchers/
56
57.. |GitHub contributors| image:: https://img.shields.io/github/contributors/hamcrest/PyHamcrest.svg?logo=github
58    :alt: GitHub contributors
59    :target: https://github.com/hamcrest/PyHamcrest/graphs/contributors/
60
61.. |GitHub issues| image:: https://img.shields.io/github/issues/hamcrest/PyHamcrest.svg?logo=github
62    :alt: GitHub issues
63    :target: https://github.com/hamcrest/PyHamcrest/issues/
64
65.. |GitHub issues-closed| image:: https://img.shields.io/github/issues-closed/hamcrest/PyHamcrest.svg?logo=github
66    :alt: GitHub issues-closed
67    :target: https://github.com/hamcrest/PyHamcrest/issues?q=is%3Aissue+is%3Aclosed
68
69.. |GitHub pull-requests| image:: https://img.shields.io/github/issues-pr/hamcrest/PyHamcrest.svg?logo=github
70    :alt: GitHub pull-requests
71    :target: https://github.com/hamcrest/PyHamcrest/pulls
72
73.. |GitHub pull-requests closed| image:: https://img.shields.io/github/issues-pr-closed/hamcrest/PyHamcrest.svg?logo=github
74    :alt: GitHub pull-requests closed
75    :target: https://github.com/hamcrest/PyHamcrest/pulls?utf8=%E2%9C%93&q=is%3Apr+is%3Aclosed
76
77.. |Lines of Code| image:: https://tokei.rs/b1/github/hamcrest/PyHamcrest
78    :alt: Lines of Code
79    :target: https://github.com/hamcrest/PyHamcrest
80
81.. |supported-implementations| image:: https://pypip.in/implementation/PyHamcrest/badge.svg?style=flat
82    :alt: Supported implementations
83    :target: https://pypi.python.org/pypi/PyHamcrest
84
85.. |scrutinizer| image:: https://img.shields.io/scrutinizer/g/hamcrest/PyHamcrest/master.svg?style=flat
86    :alt: Scrtinizer Status
87    :target: https://scrutinizer-ci.com/g/hamcrest/PyHamcrest/
88
89
90Introduction
91============
92
93PyHamcrest is a framework for writing matcher objects, allowing you to
94declaratively define "match" rules. There are a number of situations where
95matchers are invaluable, such as UI validation, or data filtering, but it is in
96the area of writing flexible tests that matchers are most commonly used. This
97tutorial shows you how to use PyHamcrest for unit testing.
98
99When writing tests it is sometimes difficult to get the balance right between
100overspecifying the test (and making it brittle to changes), and not specifying
101enough (making the test less valuable since it continues to pass even when the
102thing being tested is broken). Having a tool that allows you to pick out
103precisely the aspect under test and describe the values it should have, to a
104controlled level of precision, helps greatly in writing tests that are "just
105right." Such tests fail when the behavior of the aspect under test deviates
106from the expected behavior, yet continue to pass when minor, unrelated changes
107to the behaviour are made.
108
109Installation
110============
111
112Hamcrest can be installed using the usual Python packaging tools. It depends on
113distribute, but as long as you have a network connection when you install, the
114installation process will take care of that for you.
115
116My first PyHamcrest test
117========================
118
119We'll start by writing a very simple PyUnit test, but instead of using PyUnit's
120``assertEqual`` method, we'll use PyHamcrest's ``assert_that`` construct and
121the standard set of matchers:
122
123.. code:: python
124
125 from hamcrest import *
126 import unittest
127
128 class BiscuitTest(unittest.TestCase):
129     def testEquals(self):
130         theBiscuit = Biscuit('Ginger')
131         myBiscuit = Biscuit('Ginger')
132         assert_that(theBiscuit, equal_to(myBiscuit))
133
134 if __name__ == '__main__':
135     unittest.main()
136
137The ``assert_that`` function is a stylized sentence for making a test
138assertion. In this example, the subject of the assertion is the object
139``theBiscuit``, which is the first method parameter. The second method
140parameter is a matcher for ``Biscuit`` objects, here a matcher that checks one
141object is equal to another using the Python ``==`` operator. The test passes
142since the ``Biscuit`` class defines an ``__eq__`` method.
143
144If you have more than one assertion in your test you can include an identifier
145for the tested value in the assertion:
146
147.. code:: python
148
149 assert_that(theBiscuit.getChocolateChipCount(), equal_to(10), 'chocolate chips')
150 assert_that(theBiscuit.getHazelnutCount(), equal_to(3), 'hazelnuts')
151
152As a convenience, assert_that can also be used to verify a boolean condition:
153
154.. code:: python
155
156 assert_that(theBiscuit.isCooked(), 'cooked')
157
158This is equivalent to the ``assert_`` method of unittest.TestCase, but because
159it's a standalone function, it offers greater flexibility in test writing.
160
161
162Predefined matchers
163===================
164
165PyHamcrest comes with a library of useful matchers:
166
167* Object
168
169  * ``equal_to`` - match equal object
170  * ``has_length`` - match ``len()``
171  * ``has_property`` - match value of property with given name
172  * ``has_properties`` - match an object that has all of the given properties.
173  * ``has_string`` - match ``str()``
174  * ``instance_of`` - match object type
175  * ``none``, ``not_none`` - match ``None``, or not ``None``
176  * ``same_instance`` - match same object
177  * ``calling, raises`` - wrap a method call and assert that it raises an exception
178
179* Number
180
181  * ``close_to`` - match number close to a given value
182  * ``greater_than``, ``greater_than_or_equal_to``, ``less_than``,
183    ``less_than_or_equal_to`` - match numeric ordering
184
185* Text
186
187  * ``contains_string`` - match part of a string
188  * ``ends_with`` - match the end of a string
189  * ``equal_to_ignoring_case`` - match the complete string but ignore case
190  * ``equal_to_ignoring_whitespace`` - match the complete string but ignore extra whitespace
191  * ``matches_regexp`` - match a regular expression in a string
192  * ``starts_with`` - match the beginning of a string
193  * ``string_contains_in_order`` - match parts of a string, in relative order
194
195* Logical
196
197  * ``all_of`` - ``and`` together all matchers
198  * ``any_of`` - ``or`` together all matchers
199  * ``anything`` - match anything, useful in composite matchers when you don't care about a particular value
200  * ``is_not``, ``not_`` - negate the matcher
201
202* Sequence
203
204  * ``contains`` - exactly match the entire sequence
205  * ``contains_inanyorder`` - match the entire sequence, but in any order
206  * ``has_item`` - match if given item appears in the sequence
207  * ``has_items`` - match if all given items appear in the sequence, in any order
208  * ``is_in`` - match if item appears in the given sequence
209  * ``only_contains`` - match if sequence's items appear in given list
210  * ``empty`` - match if the sequence is empty
211
212* Dictionary
213
214  * ``has_entries`` - match dictionary with list of key-value pairs
215  * ``has_entry`` - match dictionary containing a key-value pair
216  * ``has_key`` - match dictionary with a key
217  * ``has_value`` - match dictionary with a value
218
219* Decorator
220
221  * ``calling`` - wrap a callable in a deferred object, for subsequent matching on calling behaviour
222  * ``raises`` - Ensure that a deferred callable raises as expected
223  * ``described_as`` - give the matcher a custom failure description
224  * ``is_`` - decorator to improve readability - see `Syntactic sugar` below
225
226The arguments for many of these matchers accept not just a matching value, but
227another matcher, so matchers can be composed for greater flexibility. For
228example, ``only_contains(less_than(5))`` will match any sequence where every
229item is less than 5.
230
231
232Syntactic sugar
233===============
234
235PyHamcrest strives to make your tests as readable as possible. For example, the
236``is_`` matcher is a wrapper that doesn't add any extra behavior to the
237underlying matcher. The following assertions are all equivalent:
238
239.. code:: python
240
241 assert_that(theBiscuit, equal_to(myBiscuit))
242 assert_that(theBiscuit, is_(equal_to(myBiscuit)))
243 assert_that(theBiscuit, is_(myBiscuit))
244
245The last form is allowed since ``is_(value)`` wraps most non-matcher arguments
246with ``equal_to``. But if the argument is a type, it is wrapped with
247``instance_of``, so the following are also equivalent:
248
249.. code:: python
250
251 assert_that(theBiscuit, instance_of(Biscuit))
252 assert_that(theBiscuit, is_(instance_of(Biscuit)))
253 assert_that(theBiscuit, is_(Biscuit))
254
255*Note that PyHamcrest's ``is_`` matcher is unrelated to Python's ``is``
256operator. The matcher for object identity is ``same_instance``.*
257
258
259Writing custom matchers
260=======================
261
262PyHamcrest comes bundled with lots of useful matchers, but you'll probably find
263that you need to create your own from time to time to fit your testing needs.
264This commonly occurs when you find a fragment of code that tests the same set
265of properties over and over again (and in different tests), and you want to
266bundle the fragment into a single assertion. By writing your own matcher you'll
267eliminate code duplication and make your tests more readable!
268
269Let's write our own matcher for testing if a calendar date falls on a Saturday.
270This is the test we want to write:
271
272.. code:: python
273
274 def testDateIsOnASaturday(self):
275     d = datetime.date(2008, 4, 26)
276     assert_that(d, is_(on_a_saturday()))
277
278And here's the implementation:
279
280.. code:: python
281
282 from hamcrest.core.base_matcher import BaseMatcher
283 from hamcrest.core.helpers.hasmethod import hasmethod
284
285 class IsGivenDayOfWeek(BaseMatcher):
286
287     def __init__(self, day):
288         self.day = day  # Monday is 0, Sunday is 6
289
290     def _matches(self, item):
291         if not hasmethod(item, 'weekday'):
292             return False
293         return item.weekday() == self.day
294
295     def describe_to(self, description):
296         day_as_string = ['Monday', 'Tuesday', 'Wednesday', 'Thursday',
297                          'Friday', 'Saturday', 'Sunday']
298         description.append_text('calendar date falling on ')    \
299                    .append_text(day_as_string[self.day])
300
301 def on_a_saturday():
302     return IsGivenDayOfWeek(5)
303
304For our Matcher implementation we implement the ``_matches`` method - which
305calls the ``weekday`` method after confirming that the argument (which may not
306be a date) has such a method - and the ``describe_to`` method - which is used
307to produce a failure message when a test fails. Here's an example of how the
308failure message looks:
309
310.. code:: python
311
312 assert_that(datetime.date(2008, 4, 6), is_(on_a_saturday()))
313
314fails with the message::
315
316    AssertionError:
317    Expected: is calendar date falling on Saturday
318         got: <2008-04-06>
319
320Let's say this matcher is saved in a module named ``isgivendayofweek``. We
321could use it in our test by importing the factory function ``on_a_saturday``:
322
323.. code:: python
324
325 from hamcrest import *
326 import unittest
327 from isgivendayofweek import on_a_saturday
328
329 class DateTest(unittest.TestCase):
330     def testDateIsOnASaturday(self):
331         d = datetime.date(2008, 4, 26)
332         assert_that(d, is_(on_a_saturday()))
333
334 if __name__ == '__main__':
335     unittest.main()
336
337Even though the ``on_a_saturday`` function creates a new matcher each time it
338is called, you should not assume this is the only usage pattern for your
339matcher. Therefore you should make sure your matcher is stateless, so a single
340instance can be reused between matches.
341
342
343More resources
344==============
345
346* Documentation_
347* Package_
348* Sources_
349* Hamcrest_
350
351.. _Documentation: https://pyhamcrest.readthedocs.io/
352.. _Package: http://pypi.python.org/pypi/PyHamcrest
353.. _Sources: https://github.com/hamcrest/PyHamcrest
354.. _Hamcrest: http://hamcrest.org
355