diff --git a/source/examples/testing/cigar_party.py b/source/examples/testing/cigar_party.py deleted file mode 100644 index e6863f46..00000000 --- a/source/examples/testing/cigar_party.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env python - -""" -When squirrels get together for a party, they like to have cigars. -A squirrel party is successful when the number of cigars is between -40 and 60, inclusive. Unless it is the weekend, in which case there -is no upper bound on the number of cigars. - -Return True if the party with the given values is successful, -or False otherwise. -""" - - -def cigar_party(cigars, is_weekend): - pass diff --git a/source/examples/testing/test_cigar_party.py b/source/examples/testing/test_cigar_party.py deleted file mode 100644 index 260d5f47..00000000 --- a/source/examples/testing/test_cigar_party.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env python - -""" -When squirrels get together for a party, they like to have cigars. -A squirrel party is successful when the number of cigars is between -40 and 60, inclusive. Unless it is the weekend, in which case there -is no upper bound on the number of cigars. - -Return True if the party with the given values is successful, -or False otherwise. -""" - - -# you can change this import to test different versions -from cigar_party import cigar_party -# from cigar_party import cigar_party2 as cigar_party -# from cigar_party import cigar_party3 as cigar_party - - -def test_1(): - assert cigar_party(30, False) is False - - -def test_2(): - assert cigar_party(50, False) is True - - -def test_3(): - assert cigar_party(70, True) is True - - -def test_4(): - assert cigar_party(30, True) is False - - -def test_5(): - assert cigar_party(50, True) is True - - -def test_6(): - assert cigar_party(60, False) is True - - -def test_7(): - assert cigar_party(61, False) is False - - -def test_8(): - assert cigar_party(40, False) is True - - -def test_9(): - assert cigar_party(39, False) is False - - -def test_10(): - assert cigar_party(40, True) is True - - -def test_11(): - assert cigar_party(39, True) is False diff --git a/source/examples/testing/test_random_pytest.py b/source/examples/testing/test_random_pytest.py index 6250d1b4..b9a65afd 100644 --- a/source/examples/testing/test_random_pytest.py +++ b/source/examples/testing/test_random_pytest.py @@ -8,32 +8,46 @@ import pytest -seq = list(range(10)) +example_seq = list(range(10)) + + +def test_choice(): + """ + A choice selected should be in the sequence + """ + element = random.choice(example_seq) + assert (element in example_seq) + + +def test_sample(): + """ + All the items in a sample should be in the sequence + """ + for element in random.sample(example_seq, 5): + assert element in example_seq def test_shuffle(): - # make sure the shuffled sequence does not lose any elements + """ + Make sure a shuffled sequence does not lose any elements + """ + seq = list(range(10)) random.shuffle(seq) - seq.sort() # IFyou comment this out, it will fail, so you can see output + # seq.sort() # If you comment this out, it will fail, so you can see output print("seq:", seq) # only see output if it fails assert seq == list(range(10)) def test_shuffle_immutable(): + """ + Trying to shuffle an immutable sequence raises an Exception + """ with pytest.raises(TypeError): random.shuffle((1, 2, 3)) - -def test_choice(): - element = random.choice(seq) - assert (element in seq) - - -def test_sample(): - for element in random.sample(seq, 5): - assert element in seq - - def test_sample_too_large(): + """ + Trying to sample more than exist should raise an error + """ with pytest.raises(ValueError): - random.sample(seq, 20) + random.sample(example_seq, 20) diff --git a/source/examples/testing/test_random_unitest.py b/source/examples/testing/test_random_unitest.py index f825be5b..b8a1b712 100644 --- a/source/examples/testing/test_random_unitest.py +++ b/source/examples/testing/test_random_unitest.py @@ -8,7 +8,9 @@ def setUp(self): self.seq = list(range(10)) def test_shuffle(self): - # make sure the shuffled sequence does not lose any elements + """ + make sure the shuffled sequence does not lose any elements + """ random.shuffle(self.seq) self.seq.sort() self.assertEqual(self.seq, list(range(10))) diff --git a/source/examples/testing/test_walnut_party.py b/source/examples/testing/test_walnut_party.py new file mode 100644 index 00000000..62f226dc --- /dev/null +++ b/source/examples/testing/test_walnut_party.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python + +""" +test code for the walnut party example + +Adapted from the "coding bat" site: https://codingbat.com/python + +When squirrels get together for a party, they like to have walnuts. +A squirrel party is successful when the number of walnuts is between +40 and 60, inclusive. Unless it is the weekend, in which case there +is no upper bound on the number of walnuts. + +Return True if the party with the given values is successful, +or False otherwise. +""" + + +# you can change this import to test different versions +from walnut_party import walnut_party +# from walnut_party import walnut_party2 as walnut_party +# from walnut_party import walnut_party3 as walnut_party + + +def test_1(): + assert walnut_party(30, False) is False + + +def test_2(): + assert walnut_party(50, False) is True + + +def test_3(): + assert walnut_party(70, True) is True + + +def test_4(): + assert walnut_party(30, True) is False + + +def test_5(): + assert walnut_party(50, True) is True + + +def test_6(): + assert walnut_party(60, False) is True + + +def test_7(): + assert walnut_party(61, False) is False + + +def test_8(): + assert walnut_party(40, False) is True + + +def test_9(): + assert walnut_party(39, False) is False + + +def test_10(): + assert walnut_party(40, True) is True + + +def test_11(): + assert walnut_party(39, True) is False diff --git a/source/examples/testing/walnut_party.py b/source/examples/testing/walnut_party.py new file mode 100644 index 00000000..9b195b28 --- /dev/null +++ b/source/examples/testing/walnut_party.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python + +""" +When squirrels get together for a party, they like to have walnuts. +A squirrel party is successful when the number of walnuts is between +40 and 60, inclusive. Unless it is the weekend, in which case there +is no upper bound on the number of walnuts. + +Return True if the party with the given values is successful, +or False otherwise. +""" + + +def walnut_party(walnuts, is_weekend): + pass diff --git a/source/exercises/unit_testing.rst b/source/exercises/unit_testing.rst index 72ba4057..b71653dc 100644 --- a/source/exercises/unit_testing.rst +++ b/source/exercises/unit_testing.rst @@ -8,57 +8,89 @@ Preparation ----------- In order to do unit testing, you need a framework in which to write and run your tests. -Earlier in this class, you've been adding "asserts" to your modules -- perhaps in the ``__name__ == "__main__"`` block. These are, in fact a kind of unit test. +Earlier in this class, you've been adding "asserts" to your modules -- perhaps in the ``__name__ == "__main__"`` block. These are, in fact, a kind of unit test. But as you build larger systems, you'll want a more structured way to write and run your tests. +We will use the pytest testing system for this class. +If you have not already done so -- install pytest like so: + +.. code-block:: bash + + $ python3 -m pip install pytest + +Once this is complete, you should have a ``pytest`` command you can run +at the command line: + +.. code-block:: bash + + $ pytest + ============================= test session starts ============================== + platform darwin -- Python 3.7.0, pytest-3.10.1, py-1.5.4, pluggy-0.7.1 + rootdir: /Users/Chris/temp/DrMartins, inifile: + plugins: cov-2.6.0 + collected 0 items + + ========================= no tests ran in 0.01 seconds ========================= + +If you already HAVE some tests -- you may see something different! Test Driven Development ----------------------- -Download this module: - -:download:`cigar_party.py ` +Download these files, and save them in your own students directory in the class repo: -(This is the `"cigar party" `_ problem from the codingbat site) +:download:`test_walnut_party.py <../examples/testing/test_walnut_party.py>` -and this test file: +and: -:download:`test_cigar_party.py ` +:download:`walnut_party.py <../examples/testing/walnut_party.py>` -Put them in the same directory, and make that directory your working directory. +(This is the adapted from the codingbat site: http://codingbat.com/prob/p195669) -Then try running the test file with pytest: +In the directory where you put the files, run: .. code-block:: bash - $ pytest test_cigar_party + $ pytest test_walnut_party.py + +You will get a LOT of test failures! What you've done here is the first step in what is called: - **Test Driven Development**. + **Test Driven Development** A bunch of tests exist, but the code to make them pass does not yet exist. The red you see in the terminal when we run our tests is a goad to us to write the code that fixes these tests. -Let's do that next! +The tests all failed because currently ``walnut_party()`` looks like: -Test Driven development ------------------------ +.. code-block:: python + + def walnut_party(walnuts, is_weekend): + pass + +A totally do nothing function. + + +Making tests pass +----------------- Open: -``test_cigar_party.py`` +``test_walnut_party.py`` and: -``cigar_party.py`` +``walnut_party.py`` In your editor. -Now edit ``cigar_party.py``, and each time you make a change, run the tests again. Continue until all the tests pass. +Now edit the function in ``walnut_party.py``, and each time you make a change, run the tests again. Continue until all the tests pass. + +When the tests pass -- you are done! That's the beauty of test-driven development. Doing your own: --------------- @@ -69,16 +101,23 @@ Pick another example from codingbat: Do a bit of test-driven development on it: - * run something on the web site. - * write a few tests using the examples from the site. +* Run something on the web site. +* Write a few tests using the examples from the site. +* Then write the function, and fix it 'till it passes the tests. -These tests should be in a file names ``test_something.py`` -- I usually name the test file the same as the module it tests, +These tests should be in a file named ``test_something.py`` -- I usually name the test file the same as the module it tests, with ``test_`` prepended. - * then write the function, and fix it 'till it passes the tests. +.. note:: + Technically, you can name your test files anything you want. But there are two reasons to use standard naming conventions. + One is that it is clear to anyone looking at the code what is and isn't a test module. The other is that pytest, and other testing systems, use `naming conventions `_ to find your test files. + If you name your test files: ``test_something.py`` then pytest will find them for you. And if you use the name of the module being tested: + ``test_name_of_tested_module.py`` then it will be clear which test files belong to which modules. + Do at least two of these to get the hang of the process. Also -- once you have the tests passing, look at your solution -- is there a way it could be refactored to be cleaner? + Give it a shot -- you'll know if it still works if the tests still pass! diff --git a/source/modules/Comprehensions.rst b/source/modules/Comprehensions.rst index a594c669..19408c2c 100644 --- a/source/modules/Comprehensions.rst +++ b/source/modules/Comprehensions.rst @@ -88,7 +88,40 @@ Comprehensions and map() Comprehensions are another way of expressing the "map" pattern from functional programming. -Python does have a ``map()`` function, which pre-dates comprehensions. But it does much of the same things -- and most folks think comprehensions are the more "Pythonic" way to do it. +Python does have a ``map()`` function, which pre-dates comprehensions. But it does much of the same things -- and most folks think comprehensions are the more "Pythonic" way to do it. And there is nothing that can be expressed with ``map()`` that cannot be done with a comprehension. IF youare not familiar with ``map()``, you can saftly skip this, but if you are: + +.. code-block:: python + + map(a_function, an_iterable) + +is the same as: + +.. code-block:: python + + [a_function(item), for item in an_iterable] + +In this case, the comprehension is a tad wordier than ``map()``. BUt comprehensions really shine when you do'nt already have a handy function to pass to map: + +.. code-block:: python + + [x**2 for x in an_iterable] + +To use ``map()``, you need a function: + +.. code-block:: python + + def square(x): + return x**2 + + map(square, an_iterable) + +There are shortcuts of course, including ``lambda`` (stay tuned for more about that): + +.. code-block:: python + + map(lambda x: x**2, an_iterable) + +But is that easier to read or write? What about filter? @@ -130,6 +163,8 @@ This is expressing the "filter" pattern and the "map" pattern at the same time - Get creative.... +How do I see all the built in Exceptions? + .. code-block:: python [name for name in dir(__builtin__) if "Error" in name] @@ -431,3 +466,11 @@ If you are going to immediately loop through the items created by the comprehens The "official" term is "generator expression" -- that is what you will see in the Python docs, and a lot of online discussions. I've used the term "generator comprehension" here to better make clear the association with list comprehensions. +References +---------- + +This is a nice intro to comprehensions from Trey Hunner: + +https://treyhunner.com/2015/12/python-list-comprehensions-now-in-color/ + +Trey writes a lot of good stuff -- I recommned browsing his site. diff --git a/source/modules/Testing.rst b/source/modules/Testing.rst index 7a37ae3b..f9baa7e6 100644 --- a/source/modules/Testing.rst +++ b/source/modules/Testing.rst @@ -26,9 +26,9 @@ block. * You can't do anything else when the file is executed without running tests. - This is not optimal. +This is not optimal. - Python provides testing systems to help. +Python provides testing systems to help. Standard Library: ``unittest`` @@ -38,11 +38,9 @@ The original testing system in Python. ``import unittest`` -More or less a port of ``JUnit`` from Java +More or less a port of `JUnit `_ from Java -A bit verbose: you have to write classes & methods - -(And we haven't covered that yet!) +A bit verbose: you have to write classes & methods (And we haven't covered that yet!) But here's a bit of an introduction, as you will see this in others' code. @@ -108,32 +106,31 @@ in ``test_my_mod.py``: Advantages of ``unittest`` -------------------------- +The ``unittest`` module is pretty full featured - The ``unittest`` module is pretty full featured - - It comes with the standard Python distribution, no installation required. +It comes with the standard Python distribution, no installation required. - It provides a wide variety of assertions for testing all sorts of situations. +It provides a wide variety of assertions for testing all sorts of situations. - It allows for a setup and tear down workflow both before and after all tests and before and after each test. +It allows for a setup and tear down workflow both before and after all tests and before and after each test. - It's well known and well understood. +It's well known and well understood. Disadvantages of ``unittest`` ----------------------------- - It's Object Oriented, and quite "heavyweight". +It's Object Oriented, and quite "heavyweight". - - modeled after Java's ``JUnit`` and it shows... + - modeled after Java's ``JUnit`` and it shows... - It uses the framework design pattern, so knowing how to use the features means learning what to override. +It uses the framework design pattern, so knowing how to use the features means learning what to override. - Needing to override means you have to be cautious. +Needing to override means you have to be cautious. - Test discovery is both inflexible and brittle. +Test discovery is both inflexible and brittle. - And there is no built-in parameterized testing. +And there is no built-in parameterized testing. Other Options @@ -141,22 +138,24 @@ Other Options There are several other options for running tests in Python. -* `Nose`: https://nose.readthedocs.org/ +* **Nose2**: https://github.com/nose-devs/nose2 -* `pytest`: http://pytest.org/latest/ +* **pytest**: http://pytest.org/latest/ * ... (many frameworks supply their own test runners: e.g. django) -Nose was the most common test runner when I first started learning testing, but it has been in maintaince mode for a while. +Nose was the most common test runner when I first started learning testing, but it has been in maintenance mode for a while. Even the nose2 site recommends that you consider pytest. pytest has become the defacto standard test runner for those that want a more "pythonic" test framework. -It is very capable and widely used. +pytest is very capable and widely used. For a great description of the strengths of pytest, see: `The Cleaning Hand of Pytest `_ +So we will use pytest for the rest of this class. + Installing ``pytest`` --------------------- @@ -173,7 +172,7 @@ at the command line: $ pytest -If you have any tests in your repository, that will find and run them. +If you have any tests in your repository, that command will find and run them (If you have followed the proper naming conventions). **Do you?** @@ -182,12 +181,18 @@ Pre-existing Tests Let's take a look at some examples. -in ``/examples/testing`` +Create a directory to try this out, and download: + +:download:`test_random_unitest.py <../examples/testing/test_random_unitest.py>` + +In the directory you created for that file, run: .. code-block:: bash $ pytest +It should find that test file and run it. + You can also run pytest on a particular test file: .. code-block:: bash @@ -205,17 +210,20 @@ Take a few minutes to look these files over. What is Happening Here? ----------------------- -You should have gotten results that look something like this:: +You should have gotten results that look something like this: + +.. code-block:: bash - MacBook-Pro:Session06 Chris$ pytest test_random_unitest.py + $ pytest ============================= test session starts ============================== - platform darwin -- Python 3.6.2, pytest-3.2.3, py-1.4.34, pluggy-0.4.0 - rootdir: /Users/Chris/PythonStuff/UWPCE/IntroPython-2017/examples/Session06, inifile: + platform darwin -- Python 3.7.0, pytest-3.10.1, py-1.5.4, pluggy-0.7.1 + rootdir: /Users/Chris/temp/test_temp, inifile: + plugins: cov-2.6.0 collected 3 items - test_random_unitest.py ... + test_random_unitest.py ... [100%] - =========================== 3 passed in 0.02 seconds =========================== + =========================== 3 passed in 0.06 seconds =========================== When you run the ``pytest`` command, ``pytest`` starts in your current @@ -246,60 +254,204 @@ It will run ``unittest`` tests for you, so can be used as a test runner. But in addition to finding and running tests, it makes writing tests simple, and provides a bunch of nifty utilities to support more complex testing. +Now download this file: -Test Driven Development ------------------------ +:download:`test_random_pytest.py <../examples/testing/test_random_pytest.py>` -Download these files, and save them in your own students directory in the class repo: +And run pytest again: -:download:`test_cigar_party.py <../examples/testing/test_cigar_party.py>` -and: -:download:`cigar_party.py <../examples/testing/cigar_party.py>` +.. code-block:: bash -then, in dir where you put the files, run:: + $ pytest + ============================= test session starts ============================== + platform darwin -- Python 3.7.0, pytest-3.10.1, py-1.5.4, pluggy-0.7.1 + rootdir: /Users/Chris/temp/test_temp, inifile: + plugins: cov-2.6.0 + collected 8 items - $ pytest test_cigar_party.py + test_random_pytest.py ..... [ 62%] + test_random_unitest.py ... [100%] -You will get a LOT of test failures! + =========================== 8 passed in 0.07 seconds =========================== -What we've just done here is the first step in what is called: +Note that it ran the tests in both the test files. - **Test Driven Development**. +Take a look at ``test_random_pytest.py`` -- It is essentially the same tests -- but written in native pytest style -- simple test functions. -The idea is that you write the tests first, and then write the code that passes the tests. In this case, the writing the tests part has been done for you: +pytest tests +------------ -A bunch of tests exist, but the code to make them pass does not yet exist. +The beauty of pytest is that it takes advantage of Python's dynamic nature -- you don't need to use any particular structure to write tests. -The red you see in the terminal when we run the tests is a goad to you to write the code that fixes these tests. +Any function named appropriately is a test. -The tests all failed because ``cigar_party()`` looks like: +If the function doesn't raise an error or an assertion, the test passes. It's that simple. + +Let's take a look at ``test_random_pytest.py`` to see how this works. .. code-block:: python - def cigar_party(cigars, is_weekend): - pass + import random + import pytest + +The ``random`` module is imported becasue that's what we are testing. +``pytest`` only needs to be imported if you are using its utilities -- more on this in a moment. -A totally do nothing function! +.. code-block:: python -Put real code in ``cigar_party.py`` until all the tests pass. + seq = list(range(10)) -When the tests pass -- you are done! That's the beauty of test-driven development. +Here we create a simple little sequence to use for testing. We put it in the global namespace so other functions can access it. -Trying it yourself ------------------- +Now the first tests -- simply by naming it ``test_something``, pytest will run it as a test: + +.. code-block:: python + + def test_choice(): + """ + A choice selected should be in the sequence + """ + element = random.choice(example_seq) + assert (element in example_seq) + +This is pretty straightforward. We make a random choice from the sequence, +and then assert that the selected element is, indeed, in the original sequence. + +.. code-block:: python + + def test_sample(): + """ + All the items in a sample should be in the sequence + """ + for element in random.sample(example_seq, 5): + assert element in example_seq + +And this is pretty much the same thing, except that it loops to make sure that every item returned by ``.sample`` is in the original sequence. + +Note that this will result in 5 separate assertions -- that is fine, you can have as many assertions as you like in one test function. But the test will fail on the first failed assertion -- so you only want to have closely related assertions in each test function. + +.. code-block:: python + + def test_shuffle(): + """ + Make sure a shuffled sequence does not lose any elements + """ + seq = list(range(10)) + random.shuffle(seq) + seq.sort() # If you comment this out, it will fail, so you can see output + print("seq:", seq) # only see output if it fails + assert seq == list(range(10)) + +This test is designed to make sure that ``random.shuffle`` only re-arranges the items, but doesn't add or lose any. + +In this case, the global ``example_seq`` isn't used, because ``shuffle()`` will change the sequence -- tests should never rely on or alter global state. So a new sequence is created for the test. This also allows the test to know exactly what the results should be at the end. + +Then the "real work" -- calling ``random.shuffle`` on the sequence -- this should re-arrange the elements without adding or losing any. + +Calling ``.sort()`` again should put the elements back in the order they started + +So we can then test that after shuffling and re-sorting, we have the same sequence back: -Try it a bit more, writing the tests yourself: +.. code-block:: python + + assert seq == list(range(10)) + +If that assertion passes, the test will pass. + +``print()`` and test failures +............................. + +Try commenting out the sort line: + +.. code-block:: python + + # seq.sort() # If you comment this out, it will fail, so you can see output + +And run again to see what happens. This is what I got: + +.. code-block:: bash + + $ pytest test_random_pytest.py + ============================= test session starts ============================== + platform darwin -- Python 3.7.0, pytest-3.10.1, py-1.5.4, pluggy-0.7.1 + rootdir: /Users/Chris/PythonStuff/UWPCE/PythonCertDevel/source/examples/testing, inifile: + plugins: cov-2.6.0 + collected 5 items + + test_random_pytest.py F.... [100%] + + =================================== FAILURES =================================== + _________________________________ test_shuffle _________________________________ + + def test_shuffle(): + """ + Make sure a shuffled sequence does not lose any elements + """ + seq = list(range(10)) + random.shuffle(seq) + # seq.sort() # If you comment this out, it will fail, so you can see output + print("seq:", seq) # only see output if it fails + > assert seq == list(range(10)) + E assert [4, 8, 9, 3, 2, 0, ...] == [0, 1, 2, 3, 4, 5, ...] + E At index 0 diff: 4 != 0 + E Use -v to get the full diff + + test_random_pytest.py:22: AssertionError + ----------------------------- Captured stdout call ----------------------------- + seq: [4, 8, 9, 3, 2, 0, 7, 5, 6, 1] + ====================== 1 failed, 4 passed in 0.40 seconds ====================== + +You get a lot of information when test fails. It's usually enough to tell you what went wrong. -Pick an example from codingbat: +Note that pytest didn't print out the results of the print statement when the test passed, but when it failed, it printed it (under "Captured stdout call"). This means you can put diagnostic print calls in your tests, and they will not clutter up the output when they are not needed. - `codingbat `_ +Testing for Exceptions +...................... -Do a bit of test-driven development on it: +One of the things you might want to test about your code is that it raises an exception when it should -- and that the exception it raises is the correct one. + +In this example, if you try to call ``random.shuffle`` with an immutable sequence, such as a tuple, it should raise a ``TypeError``. Since raising an exception will generally stop the code (and cause a test to fail), we can't use an assertion to test for this. + +pytest provides a "context manager", ``pytest.raises``, that can be used to test for exceptions. The test will pass if and only if the specified Exception is raised by the enclosed code. You use it like so: + +.. code-block:: python + + def test_shuffle_immutable(): + """ + Trying to shuffle an immutable sequence raises an Exception + """ + with pytest.raises(TypeError): + random.shuffle((1, 2, 3)) + +The ``with`` block is how you use a context manager -- it will run the code enclosed, and perform various actions at the end of the code, or when an exception is raised. +This is the same ``with`` as used to open files. In that case, it is used to assure that the file is properly closed when you are done with it. In this case, the ``pytest.raises`` context manager captures any exceptions, and raises an ``AssertionError`` if no exception is raised, or if the wrong exception is raised. + +In this case, the test will only pass if a ``TypeError`` is raised by the call to ``random.shuffle`` with a tuple as an argument. + +The next test: + +.. code-block:: python + + def test_sample_too_large(): + """ + Trying to sample more than exist should raise an error + """ + with pytest.raises(ValueError): + random.sample(example_seq, 20) + +is very similar, except that this time, a ValueError has to be raised for the test to pass. + +pytest provides a number of other features for fixtures, parameterized tests, test classes, configuration, shared resources, etc. +But simple test functions like this will get you very far. + + +Test Driven Development +----------------------- - * run something on the web site. - * write a few tests using the examples from the site. - * then write the function, and fix it 'till it passes the tests. +Test Driven Development or "TDD", is a development process where you write tests to assure that your code works, *before* you write the actual code. -Do at least two of these... +This is a very powerful approach, as it forces you to think carefully about exactly what your code should do before you start to write it. It also means that you know when you code is working, and you can refactor it in the future with assurance that you haven't broken it. +Give this exercise a try to get the idea: +:ref:`exercise_unit_testing` diff --git a/source/solutions/Lesson01/codingbat/Logic-1/cigar_party.py b/source/solutions/Lesson01/codingbat/Logic-1/cigar_party.py deleted file mode 100755 index ad7df22c..00000000 --- a/source/solutions/Lesson01/codingbat/Logic-1/cigar_party.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python - - -def cigar_party(cigars, is_weekend): - """ - basic solution - """ - if is_weekend and cigars >= 40: - return True - elif 40 <= cigars <= 60: - return True - return False - - -def cigar_party2(cigars, is_weekend): - """ - some direct return of bool result - """ - if is_weekend: - return (cigars >= 40) - return (cigars >= 40 and cigars <= 60) - - -def cigar_party3(cigars, is_weekend): - """ - conditional expression - """ - return (cigars >= 40) if is_weekend else (cigars >= 40 and cigars <= 60) - -if __name__ == "__main__": - # some tests - - assert cigar_party(30, False) is False - assert cigar_party(50, False) is True - assert cigar_party(70, True) is True - assert cigar_party(30, True) is False - assert cigar_party(50, True) is True - assert cigar_party(60, False) is True - assert cigar_party(61, False) is False - assert cigar_party(40, False) is True - assert cigar_party(39, False) is False - assert cigar_party(40, True) is True - assert cigar_party(39, True) is False - - print("All tests passed") diff --git a/source/solutions/Lesson01/codingbat/Logic-1/walnut_party.py b/source/solutions/Lesson01/codingbat/Logic-1/walnut_party.py new file mode 100755 index 00000000..3bdae1d8 --- /dev/null +++ b/source/solutions/Lesson01/codingbat/Logic-1/walnut_party.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python + +""" +adapted from coding bat: https://codingbat.com/python +""" + + +def walnut_party(walnuts, is_weekend): + """ + basic solution + """ + if is_weekend and walnuts >= 40: + return True + elif 40 <= walnuts <= 60: + return True + return False + + +def walnut_party2(walnuts, is_weekend): + """ + Direct return of bool result + """ + if is_weekend: + return (walnuts >= 40) + return (walnuts >= 40 and walnuts <= 60) + + +def walnut_party3(walnuts, is_weekend): + """ + Conditional expression + """ + return (walnuts >= 40) if is_weekend else (walnuts >= 40 and walnuts <= 60) + +if __name__ == "__main__": + # some tests + + assert walnut_party(30, False) is False + assert walnut_party(50, False) is True + assert walnut_party(70, True) is True + assert walnut_party(30, True) is False + assert walnut_party(50, True) is True + assert walnut_party(60, False) is True + assert walnut_party(61, False) is False + assert walnut_party(40, False) is True + assert walnut_party(39, False) is False + assert walnut_party(40, True) is True + assert walnut_party(39, True) is False + + print("All tests passed") diff --git a/source/solutions/Lesson06/.gitignore b/source/solutions/Lesson06/.gitignore new file mode 100644 index 00000000..8035ab50 --- /dev/null +++ b/source/solutions/Lesson06/.gitignore @@ -0,0 +1,3 @@ +*.txt + + diff --git a/source/solutions/Lesson06/cigar_party.py b/source/solutions/Lesson06/cigar_party.py deleted file mode 100644 index 992d99bd..00000000 --- a/source/solutions/Lesson06/cigar_party.py +++ /dev/null @@ -1,15 +0,0 @@ - -""" -When squirrels get together for a party, they like to have cigars. -A squirrel party is successful when the number of cigars is between -40 and 60, inclusive. Unless it is the weekend, in which case there -is no upper bound on the number of cigars. - -Return True if the party with the given values is successful, -or False otherwise. -""" - - -def cigar_party(num, weekend): - return num >= 40 and (num <= 60 or weekend) - diff --git a/source/solutions/Lesson06/test_cigar_party.py b/source/solutions/Lesson06/test_cigar_party.py deleted file mode 100644 index 80bc0920..00000000 --- a/source/solutions/Lesson06/test_cigar_party.py +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env python - -""" -When squirrels get together for a party, they like to have cigars. -A squirrel party is successful when the number of cigars is between -40 and 60, inclusive. Unless it is the weekend, in which case there -is no upper bound on the number of cigars. - -Return True if the party with the given values is successful, -or False otherwise. -""" - - -# you can change this import to test different versions -from cigar_party import cigar_party -# from cigar_party import cigar_party2 as cigar_party -# from cigar_party import cigar_party3 as cigar_party - - -def test_1(): - assert cigar_party(30, False) is False - - -def test_2(): - - assert cigar_party(50, False) is True - - -def test_3(): - - assert cigar_party(70, True) is True - - -def test_4(): - assert cigar_party(30, True) is False - - -def test_5(): - assert cigar_party(50, True) is True - - -def test_6(): - assert cigar_party(60, False) is True - - -def test_7(): - assert cigar_party(61, False) is False - - -def test_8(): - assert cigar_party(40, False) is True - - -def test_9(): - assert cigar_party(39, False) is False - - -def test_10(): - assert cigar_party(40, True) is True - - -def test_11(): - assert cigar_party(39, True) is False - diff --git a/source/solutions/Lesson06/test_walnut_party.py b/source/solutions/Lesson06/test_walnut_party.py new file mode 100644 index 00000000..5bd7ae45 --- /dev/null +++ b/source/solutions/Lesson06/test_walnut_party.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python + +""" +When squirrels get together for a party, they like to have walnuts. +A squirrel party is successful when the number of walnuts is between +40 and 60, inclusive. Unless it is the weekend, in which case there +is no upper bound on the number of walnuts. + +Return True if the party with the given values is successful, +or False otherwise. +""" + + +# you can change this import to test different versions +from walnut_party import walnut_party +# from walnut_party import walnut_party2 as walnut_party +# from walnut_party import walnut_party3 as walnut_party + + +def test_1(): + assert walnut_party(30, False) is False + + +def test_2(): + + assert walnut_party(50, False) is True + + +def test_3(): + + assert walnut_party(70, True) is True + + +def test_4(): + assert walnut_party(30, True) is False + + +def test_5(): + assert walnut_party(50, True) is True + + +def test_6(): + assert walnut_party(60, False) is True + + +def test_7(): + assert walnut_party(61, False) is False + + +def test_8(): + assert walnut_party(40, False) is True + + +def test_9(): + assert walnut_party(39, False) is False + + +def test_10(): + assert walnut_party(40, True) is True + + +def test_11(): + assert walnut_party(39, True) is False diff --git a/source/solutions/Lesson06/walnut_party.py b/source/solutions/Lesson06/walnut_party.py new file mode 100644 index 00000000..781f71be --- /dev/null +++ b/source/solutions/Lesson06/walnut_party.py @@ -0,0 +1,14 @@ + +""" +When squirrels get together for a party, they like to have walnuts. +A squirrel party is successful when the number of walnuts is between +40 and 60, inclusive. Unless it is the weekend, in which case there +is no upper bound on the number of walnuts. + +Return True if the party with the given values is successful, +or False otherwise. +""" + + +def walnut_party(num, weekend): + return num >= 40 and (num <= 60 or weekend) diff --git a/source/solutions/codingbat/Logic-1/cigar_party.py b/source/solutions/codingbat/Logic-1/cigar_party.py deleted file mode 100755 index ad7df22c..00000000 --- a/source/solutions/codingbat/Logic-1/cigar_party.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python - - -def cigar_party(cigars, is_weekend): - """ - basic solution - """ - if is_weekend and cigars >= 40: - return True - elif 40 <= cigars <= 60: - return True - return False - - -def cigar_party2(cigars, is_weekend): - """ - some direct return of bool result - """ - if is_weekend: - return (cigars >= 40) - return (cigars >= 40 and cigars <= 60) - - -def cigar_party3(cigars, is_weekend): - """ - conditional expression - """ - return (cigars >= 40) if is_weekend else (cigars >= 40 and cigars <= 60) - -if __name__ == "__main__": - # some tests - - assert cigar_party(30, False) is False - assert cigar_party(50, False) is True - assert cigar_party(70, True) is True - assert cigar_party(30, True) is False - assert cigar_party(50, True) is True - assert cigar_party(60, False) is True - assert cigar_party(61, False) is False - assert cigar_party(40, False) is True - assert cigar_party(39, False) is False - assert cigar_party(40, True) is True - assert cigar_party(39, True) is False - - print("All tests passed")