Where we're going

Format

* Show your work! Tweet @geoffhing with pastebin or gist links when you get the solution

Test driven development (TDD)

Write (failing) test

Make it pass

Refactor

Test driven development (TDD)

unit tests

test case

test runner

assertion

On deadline?!?

Simple designs

Confidence

Ch-ch-ch-ch-changes

Manual testing is slow

If you look at open-source projects on GitHub, you'll often see indicators of test coverage for the code base (the percentage of functions, statements, branches or conditions that have tests written for them). It seems unimaginable to be able to get good test coverage in a newsroom environment with simultaneous, short-lived projects on deadline.
However, testing is often a worthwhile investment.
* Helps you reason about your design
* Better to quickly test the parts that you're uncertain about directly than re-running an entire module
* Finding and fixing bugs in production code sucks
* Save time testing or worrying about changes later (or worse, not making changes for fear of breaking)
In my work at the Tribune I usually:
* Test the hard parts
* Write tests to get unstuck, warm up
* Write imaginary tests (don't actually write tests but think about how you would)

Testing tools

There are a wealth of tools for writing tests in Python as well as a variety of testing paradigms. Today we'll focus on unit testing using the `unittest` package which is part of the Python Standard Library and `nose`, a test runner, framework and useful set of utilities for testing in Python.

The "mixin" pattern is a common use of multiple inheritance. A "mixin" is a class that is always used as base class and is not intended to be instantiated directly. Its methods are usually intended to be called by methods in the subclasses.

Let's use a mixin!

Use the ResultsTestMixin class to avoid duplicate code in the test case classes in tests/tests_oo.py

The selection of which implementation of an object's method is called the Method Resolution Order (MRO). For simple class hierarchies, it's safe to say methods are resolved from left to right as the bases are specified in the class definition. The actual algorithm used to resolve the MRO is called the "C3 superclass linearization" is pretty complex. Generally, it's a good idea to keep the inheritance hierarchy simple when using multiple inheritance. When combining complex functionalities, Composition might be a preferable design pattern.

We can alter the behavior of BaseAPResultCollection by specifying a different type of object for client. In this example, we have a client that loads files from the filesystem instead of a remote FTP server.

Delegation

Delegation is a design pattern based on composition where methods on the outer class call through to the member class unless overridden.
In this example we implement the "magic method" `__getattr__` to get the requested attribute from the member class.

This shows why UpperOut is a good choice for delegation. We never really instantiate file objects. Rather, we get an instance using open().

Let's compose!

Implement the methods in pycar_advanced/oo.py to use composition to implement a basic election results fetcher/loader. Then these tests should pass:

nosetests tests/test_oo.py

Functional Programming

Iterators

Generators

List comprehensions

Decorators

So far, we've looked at object-oriented features and design patterns in Python. However, Python is a multi-paradigm language and we can also write code that is "functional". The "functional" programming paradigm decomposes a task into a set of functions. Functions should take only inputs and produce outputs and not have any side-effects. Writing programs using the functional paradigm helps make code more modular, easier to test and easier to reason about.

Generators

Generators

class Configuration(object):
# ...
@property
def exports(self):
if not isinstance(self.data, dict):
raise FreezeException("The root element of the freeze file needs to be a hash")
if not isinstance(self.data.get('exports'), list):
raise FreezeException("The freeze file needs to have a list of exports")
common = self.data.get('common', {})
for export in self.data.get('exports'):
yield Export(common, export)

Dataset is a really useful Python package for loading structured data into a relational database, querying the data, and exporting the results. It has a concept of a "freeze file" that defines queries that will be exported to CSV for JSON. The exports method is a generator that returns an iterator of Export objects that encapsulate the configuration for the queries and export format.

Let's make a generator

Implement the generate_results() function in pycar_advanced/functional.py so that the following command shows passing tests.

Functions can return functions

Just as functions can be arguments to other functions, functions can also return functions. This function returns a function that prints the name specified by the `first_name` and `last_name` arguments.
This is an example of a "closure". The instance of `name_fn` remembers the enclosing namespace (i.e. the values of `first_name` and `last_name`) from when it was declared.
FYI, [closures in Python are late-binding](http://docs.python-guide.org/en/latest/writing/gotchas/#late-binding-closures).

Decorators

This is an example of a Fabric task that we use to deploy our elections application. It uses Fabric's `roles` decorator to define the hosts that this task will be run on.
From http://docs.python-guide.org/en/latest/writing/structure/#decorators:
This mechanism is useful for separating concerns and avoiding external un-related logic ‘polluting’ the core logic of the function or method. A good example of a piece of functionality that is better handled with decoration is memoization or caching: you want to store the results of an expensive function in a table and use them directly instead of recomputing them when they have already been computed. This is clearly not part of the function logic.