"""Python Cookbook

Chapter 11, Examples from the text.

•	Using docstrings for testing
•	Testing functions that raise exceptions
•	Handling common doctest issues
•   Unit testing with the unittest module
•	Combining unittest and doctest tests
•   Unit testing with the pytest module
•   Combining pytest and doctest tests
•	Testing things that involve dates or times
•	Testing things that involve randomness
•	Mocking external resources

"""

#  Testing functions that raise exceptions

>>> from Chapter_11.ch11_r01 import binom

>>> binom(52, 5)
2598960

>>> from Chapter_11.ch11_r01 import Summary

>>> s = Summary()
>>> s.add(8)
>>> s.add(9)
>>> s.add(9)
>>> round(s.mean, 2)
8.67
>>> s.median
9

>>> print(str(s))
mean = 8.67
median = 9

>>> binom(52, 5)
2598960
>>> binom(52, 0)
1
>>> binom(52, 52)
1

>>> binom(0, 0)
1

>>> binom(52, 5)
2598960

>>> binom(5, 52)
Traceback (most recent call last):
  File "/Users/slott/miniconda3/envs/cookbook/lib/python3.8/doctest.py", line 1328, in __run
    compileflags, 1), test.globs)
  File "<doctest examples.txt[15]>", line 1, in <module>
    binom(5, 52)
  File "/Users/slott/Documents/Writing/Python/Python Cookbook 2e/Code/Chapter_11.ch11_r01.py", line 24, in binom
    return factorial(n) // (factorial(k) * factorial(n-k))
ValueError: factorial() not defined for negative values


>>> binom(52, -5)  # doctest: +ELLIPSIS
Traceback (most recent call last):
    ...
ValueError: factorial() not defined for negative values

# Handling common doctest issues

>>> from io import StringIO
>>> mock_file = StringIO('''lat,lon,date,time
... 32.8321,-79.9338,2012-11-27,09:15:00
... ''')

>>> from Chapter_11.ch11_r03 import raw_reader
>>> row_iter = iter(raw_reader(mock_file))
>>> row = next(row_iter)
>>> row
Row(date='2012-11-27', lat='32.8321', lon='-79.9338', time='09:15:00')


>>> from math import erf, sqrt
>>> def pdf(n):
...     """
...     The cumulative distribution function for the standard normal
...     distribution.
...
...     :param n: number of standard deviations
...     :returns: cumulative fraction of values below n.
...
...     Examples:
...     """
...     return (1+erf(n/sqrt(2)))/2

>>> round(pdf(0), 3)
0.5
>>> round(pdf(-1), 3)
0.159
>>> round(pdf(+1), 3)
0.841

# Creating separate test modules and packages

>>> s = Summary()
>>> s.add(8)
>>> s.add(9)
>>> s.add(9)
>>> round(s.mean, 2)
8.67
>>> s.median
9
>>> print(str(s))
mean = 8.67
median = 9

# Testing things that involve dates or times

>>> from unittest.mock import *
>>> dumb_function = Mock(return_value=12)
>>> dumb_function(9)
12
>>> dumb_function(18)
12

>>> dumb_function.mock_calls
[call(9), call(18)]

>>> dumb_function.assert_called_with(18)

>>> dumb_function.assert_has_calls([call(9), call(18)])

#	Testing things that involve randomness

# Lines 890 to 896
>>> from unittest.mock import *
>>> mocked_iterator = Mock(side_effect=[11, 13])
>>> mocked_iterator()
11
>>> mocked_iterator()
13
>>> mocked_iterator()
Traceback (most recent call last):
  File "/Users/slott/miniconda3/envs/cookbook/lib/python3.8/doctest.py", line 1328, in __run
    compileflags, 1), test.globs)
  File "<doctest examples.txt[53]>", line 1, in <module>
    mocked_iterator(31)
  File "/Users/slott/miniconda3/envs/cookbook/lib/python3.8/unittest/mock.py", line 965, in __call__
    return _mock_self._mock_call(*args, **kwargs)
  File "/Users/slott/miniconda3/envs/cookbook/lib/python3.8/unittest/mock.py", line 1027, in _mock_call
    result = next(effect)
StopIteration

>>> mocked_iterator.mock_calls
[call(), call(), call()]


# Mocking external resources

>>> a_mock = Mock(side_effect=RuntimeError)
>>> a_mock(1)
Traceback (most recent call last):
...
RuntimeError

>>> b_mock = Mock(side_effect=[42, RuntimeError])
>>> b_mock(1)
42
>>> b_mock(2)
Traceback (most recent call last):
...
RuntimeError

>>> from unittest.mock import sentinel
>>> sentinel.GOOD_DATA != sentinel.CORRUPT_DATA
True
>>> id(sentinel.GOOD_DATA) != id(sentinel.CORRUPT_DATA)
True
