diff options
-rw-r--r-- | slides/advanced_python/oop_unittest.tex | 117 | ||||
-rw-r--r-- | slides/advanced_python/slides/oop.tex | 190 | ||||
-rw-r--r-- | slides/test_driven_development/tdd_simple.tex | 342 |
3 files changed, 647 insertions, 2 deletions
diff --git a/slides/advanced_python/oop_unittest.tex b/slides/advanced_python/oop_unittest.tex new file mode 100644 index 0000000..b8e21bf --- /dev/null +++ b/slides/advanced_python/oop_unittest.tex @@ -0,0 +1,117 @@ +\documentclass[12pt,presentation]{beamer} +\usepackage[utf8]{inputenc} +\usepackage[T1]{fontenc} +\usepackage{fixltx2e} +\usepackage{graphicx} +\usepackage{longtable} +\usepackage{float} +\usepackage{wrapfig} +\usepackage{soul} +\usepackage{textcomp} +\usepackage{marvosym} +\usepackage{wasysym} +\usepackage{latexsym} +\usepackage{amssymb} +\usepackage{hyperref} +\tolerance=1000 +\usepackage[english]{babel} \usepackage{ae,aecompl} +\usepackage{mathpazo,courier,euler} \usepackage[scaled=.95]{helvet} +\usepackage{listings} +\lstset{language=Python, basicstyle=\ttfamily\bfseries, +commentstyle=\color{red}\itshape, stringstyle=\color{green}, +showstringspaces=false, keywordstyle=\color{blue}\bfseries} +\providecommand{\alert}[1]{\textbf{#1}} + +\title{SEES: OOP and more testing} +\author{FOSSEE} + +\usetheme{Warsaw}\usecolortheme{default}\useoutertheme{infolines}\setbeamercovered{transparent} + +\AtBeginSection[] +{ + \begin{frame}<beamer> + \frametitle{Outline} + \tableofcontents[currentsection] + \end{frame} +} + +\begin{document} + +\maketitle + +\begin{frame} +\frametitle{Outline} +\setcounter{tocdepth}{3} +\tableofcontents +\end{frame} + +\include{slides/oop} + + +\section{\texttt{unittest} framework} + +\begin{frame}[fragile] + \frametitle{\texttt{unittest}} + \begin{itemize} + \item \texttt{unittest} framework can efficiently automate tests + \item Easily initialize code and data for executing the specific + tests + \item Cleanly shut them down once the tests are executed + \item Easily aggregate tests into collections and improved reporting + \end{itemize} +\end{frame} + + +\begin{frame}[fragile] + \frametitle{\texttt{unittest}ing \texttt{gcd.py}} + \begin{itemize} + \item Subclass the \texttt{TestCase} class in \texttt{unittest} + \item Place all the test code as methods of this class + \item Place the code in \texttt{test\_gcd.py} + \end{itemize} +\end{frame} + +\begin{frame}[fragile,allowframebreaks] + \frametitle{\texttt{test\_gcd.py}} +\small +\begin{lstlisting} +import gcd +import unittest + +class TestGcdFunction(unittest.TestCase): + def setUp(self): + # Called before each test case. + print "In setUp" + + def tearDown(self): + print "In tearDown" + + def test_gcd(self): + self.assertEqual(gcd.gcd(45, 5), 5) + self.assertEqual(gcd.gcd(45, 5), 5) + + def test_gcd_correctly_handles_floats(self): + # Write appropriate tests here. + pass + +if __name__ == '__main__': + unittest.main() +\end{lstlisting} +\end{frame} + +\begin{frame}[fragile] + \frametitle{\texttt{test\_gcd.py}} + \begin{itemize} + \item \texttt{setUp} -- called before every \texttt{test\_*} method + \item \texttt{tearDown} -- called after every test + \item \texttt{setUp} and \texttt{tearDown} -- useful to perform common + operations, make a temporary directory, delete it when done etc. + \item \texttt{test\_gcd} -- actual test code + \item \texttt{assertEqual} -- compare actual result with expected one + \item Also see: \url{docs.python.org/2.7/library/unittest.html} + \end{itemize} +\end{frame} + + + +\end{document} diff --git a/slides/advanced_python/slides/oop.tex b/slides/advanced_python/slides/oop.tex index bdb983c..e1e96f8 100644 --- a/slides/advanced_python/slides/oop.tex +++ b/slides/advanced_python/slides/oop.tex @@ -13,6 +13,190 @@ \end{frame} \begin{frame}[fragile] + \frametitle{Classes: the big picture} + \begin{itemize} + \item Lets you create new data types + \item Class is a template for an object belonging to that class + \item Note: in Python a class is also an object + \item Instantiating a class creates an instance (an object) + \item An instance encapsulates the state (data) and behavior + (methods) + \item Allows you to define an inheritance hierarchy + \begin{itemize} + \item ``A Honda car \alert{is a} car.'' + \item ``A car \alert{is an} automobile.'' + \item ``A Python \alert{is a} reptile.'' + \end{itemize} + \item Programmers need to think OO + \end{itemize} +\end{frame} + +\begin{frame}[fragile] + \frametitle{Classes: what's the big deal?} + \begin{itemize} + \item Lets you create objects that mimic a real problem being + simulated + \item Makes problem solving more natural and elegant + \item Easier to create code + \item Allows for code-reuse + \item Polymorphism + \end{itemize} +\end{frame} + +\begin{frame}[fragile] + \frametitle{Class definition and instantiation} + \begin{itemize} + \item Class definitions when executed create class objects + \item Instantiating the class object creates an instance of the + class + \end{itemize} +\footnotesize +\begin{lstlisting} +class Foo(object): + pass +# class object created. + +# Create an instance of Foo. +f = Foo() +# Can assign an attribute to the instance +f.a = 100 +print f.a +100 +\end{lstlisting} +\end{frame} + +\begin{frame}[fragile] + \frametitle{Classes \ldots} + \begin{itemize} + \item All attributes are accessed via the \texttt{object.attribute} + syntax + \item Both class and instance attributes are supported + \item \emph{Methods} represent the behavior of an object: crudely + think of them as functions ``belonging'' to the object + \item All methods in Python are ``virtual'' + \item Inheritance through subclassing + \item Multiple inheritance is supported + \item No special public and private attributes: only good + conventions + \begin{itemize} + \item \verb+object.public()+: public + \item \verb+object._private()+ \& \verb+object.__priv()+: + non-public + \end{itemize} + \end{itemize} +\end{frame} + +\begin{frame}[fragile] + \frametitle{Classes: examples} +\begin{lstlisting} +class MyClass(object): + """Example class (this is the class docstring).""" + i = 12345 # A class attribute + def f(self): + """This is the method docstring""" + return 'hello world' + +>>> a = MyClass() # creates an instance +>>> a.f() +'hello world' +>>> # a.f() is equivalent to MyClass.f(a) +... # This also explains why f has a 'self' argument. +... MyClass.f(a) +'hello world' +\end{lstlisting} +\end{frame} + +\begin{frame}[fragile] + \frametitle{Classes (continued)} + \begin{itemize} + \item \texttt{self} is \alert{conventionally} the first argument for a + method + \item In previous example, \texttt{a.f} is a method object + \item When \texttt{a.f} is called, it is passed the instance \texttt{a} as + the first argument + \item If a method called \verb+__init__+ exists, it is called when + the object is created + \item If a method called \verb+__del__+ exists, it is called before + the object is garbage collected + \item Instance attributes are set by simply ``setting'' them in + \texttt{self} + \item Other special methods (by convention) like \verb+__add__+ let + you define numeric types: + {\footnotesize \url{https://docs.python.org/2.7/reference/datamodel.html} + } + \end{itemize} +\end{frame} + +\begin{frame}[fragile] + \frametitle{Classes: examples} +\begin{lstlisting} +class Bag(MyClass): # Shows how to derive classes + def __init__(self): # called on object creation. + self.data = [] # an instance attribute + def add(self, x): + self.data.append(x) + def addtwice(self, x): + self.add(x) + self.add(x) +>>> a = Bag() +>>> a.f() # Inherited method +'hello world' +>>> a.add(1); a.addtwice(2) +>>> a.data +[1, 2, 2] +\end{lstlisting} +\end{frame} + +\begin{frame}[fragile] + \frametitle{Derived classes} + \begin{itemize} + \item Call the parent's \verb+__init__+ if needed + \item If you don't need a new constructor, no need to define it in subclass + \item Can also use the \verb+super+ built-in function + \end{itemize} +\begin{lstlisting} +class AnotherBag(Bag): + def __init__(self): + # Must call parent's __init__ explicitly + Bag.__init__(self) + # Alternatively use this: + super(AnotherBag, self).__init__() + # Now setup any more data. + self.more_data = [] +\end{lstlisting} +\end{frame} + +\begin{frame}[fragile] + \frametitle{Classes: polymorphism} +\begin{lstlisting} +class Drawable(object): + def draw(self): + # Just a specification. + pass +\end{lstlisting} +\mode<presentation>{\pause} +\begin{lstlisting} +class Square(Drawable): + def draw(self): + # draw a square. +class Circle(Drawable): + def draw(self): + # draw a circle. +\end{lstlisting} +\end{frame} +\begin{frame}[fragile] + \frametitle{Classes: polymorphism} +\begin{lstlisting} +class Artist(Drawable): + def draw(self): + for obj in self.drawables: + obj.draw() +\end{lstlisting} +\end{frame} + + + +\begin{frame}[fragile] \frametitle{Example: Managing Talks} \begin{itemize} \item A list of talks at a conference @@ -191,8 +375,10 @@ class Tutorial(Talk): """A class for the tutorials.""" - def __init__(self, speaker, title, tags, handson=True): - Talk.__init__(self, speaker, title, tags) + def __init__(self, speaker, title, tags, + handson=True): + Talk.__init__(self, speaker, title, + tags) self.handson = handson def is_handson(self): diff --git a/slides/test_driven_development/tdd_simple.tex b/slides/test_driven_development/tdd_simple.tex new file mode 100644 index 0000000..ae57c69 --- /dev/null +++ b/slides/test_driven_development/tdd_simple.tex @@ -0,0 +1,342 @@ +\documentclass[12pt,presentation]{beamer} +\usepackage[utf8]{inputenc} +\usepackage[T1]{fontenc} +\usepackage{fixltx2e} +\usepackage{graphicx} +\usepackage{longtable} +\usepackage{float} +\usepackage{wrapfig} +\usepackage{soul} +\usepackage{textcomp} +\usepackage{marvosym} +\usepackage{wasysym} +\usepackage{latexsym} +\usepackage{amssymb} +\usepackage{hyperref} +\tolerance=1000 +\usepackage[english]{babel} \usepackage{ae,aecompl} +\usepackage{mathpazo,courier,euler} \usepackage[scaled=.95]{helvet} +\usepackage{listings} +\lstset{language=Python, basicstyle=\ttfamily\bfseries, +commentstyle=\color{red}\itshape, stringstyle=\color{green}, +showstringspaces=false, keywordstyle=\color{blue}\bfseries} +\providecommand{\alert}[1]{\textbf{#1}} + +\title{SEES: Test Driven Development} +\author{FOSSEE} + +\usetheme{Warsaw}\usecolortheme{default}\useoutertheme{infolines}\setbeamercovered{transparent} + +\AtBeginSection[] +{ + \begin{frame}<beamer> + \frametitle{Outline} + \tableofcontents[currentsection] + \end{frame} +} + +\begin{document} + +\maketitle + +\begin{frame} +\frametitle{Outline} +\setcounter{tocdepth}{3} +\tableofcontents +\end{frame} + +\section{Introduction} + +\begin{frame} + \frametitle{Objectives} + At the end of this section, you will be able to: + \begin{itemize} + \item Write your code using the TDD paradigm. + \item Use the nose module to test your code. + \end{itemize} +\end{frame} + +\begin{frame} + \frametitle{What is TDD?} + The basic steps of TDD are roughly as follows -- + \begin{enumerate} + \item<1-> Decide upon feature to implement and methodology of + testing + \item<2-> Write tests for feature decided upon + \item<3-> Just write enough code, so that the test can be run, but it fails. + \item<4-> Improve the code, to just pass the test and at the same time + passing all previous tests. + \item<5-> Run the tests to see, that all of them run successfully. + \item<6-> Refactor the code you've just written -- optimize the algorithm, + remove duplication, add documentation, etc. + \item<7-> Run the tests again, to see that all the tests still pass. + \item<8-> Go back to 1. + \end{enumerate} +\end{frame} + +\section{First Test} + +\begin{frame}[fragile] + \frametitle{First Test -- GCD} + \begin{itemize} + \item simple program -- GCD of two numbers + \item What are our code units? + \begin{itemize} + \item Only one function \texttt{gcd} + \item Takes two numbers as arguments + \item Returns one number, which is their GCD + \end{itemize} +\begin{lstlisting} +c = gcd(44, 23) +\end{lstlisting} + \item c will contain the GCD of the two numbers. + \end{itemize} +\end{frame} + +\begin{frame}[fragile] + \frametitle{Test Cases} + \begin{itemize} + \item Important to have test cases and expected outputs even before + writing the first test! + \item $a=48$, $b=48$, $GCD=48$ + \item $a=44$, $b=19$, $GCD=1$ + \item Tests are just a series of assertions + \item True or False, depending on expected and actual behavior + \end{itemize} + +\end{frame} + +\begin{frame}[fragile] + \frametitle{Test Cases -- general idea} +\begin{lstlisting} +tc1 = gcd(48, 64) +if tc1 != 16: + print "Failed for a=48, b=64. Expected 16. \ + Obtained %d instead." % tc1 + exit(1) + +tc2 = gcd(44, 19) +if tc2 != 1: + print "Failed for a=44, b=19. Expected 1. \ + Obtained %d instead." % tc2 + exit(1) + +print "All tests passed!" +\end{lstlisting} +\begin{itemize} +\item The function \texttt{gcd} doesn't even exist! +\end{itemize} +\end{frame} + + +\begin{frame}[fragile] + \frametitle{Test Cases -- code} + \begin{itemize} + \item Let us make it a function! + \item Use assert! + \end{itemize} +\end{frame} + +\begin{frame}[fragile] + \frametitle{Test Cases -- code} +\begin{lstlisting} +# gcd.py +def test_gcd(): + assert gcd(48, 64) == 16 + assert gcd(44, 19) == 1 + +test_gcd() +\end{lstlisting} +\end{frame} + +\begin{frame}[fragile] + \frametitle{Stubs} + \begin{itemize} + \item First write a very minimal definition of \texttt{gcd} + \begin{lstlisting} +def gcd(a, b): + pass + \end{lstlisting} + \item Written just, so that the tests can run + \item Obviously, the tests are going to fail + \end{itemize} +\end{frame} + +\begin{frame}[fragile] + \frametitle{\texttt{gcd.py}} +\begin{lstlisting} +def gcd(a, b): + pass + +def test_gcd(): + assert gcd(48, 64) == 16 + assert gcd(44, 19) == 1 + +if __name__ == '__main__': + test_gcd() +\end{lstlisting} +\end{frame} + +\begin{frame}[fragile] + \frametitle{First run} +\begin{lstlisting} +$ python gcd.py +Traceback (most recent call last): + File "gcd.py", line 9, in <module> + test_gcd() + File "gcd.py", line 5, in test_gcd + assert gcd(48, 64) == 16 +AssertionError +\end{lstlisting} %$ + + \begin{itemize} + \item We have our code unit stub, and a failing test. + \item The next step is to write code, so that the test just passes. + \end{itemize} +\end{frame} + +\begin{frame}[fragile] + \frametitle{Euclidean Algorithm} + \begin{itemize} + \item Modify the \texttt{gcd} stub function + \item Then, run the script to see if the tests pass. + \end{itemize} +\begin{lstlisting} +def gcd(a, b): + if a == 0: + return b + while b != 0: + if a > b: + a = a - b + else: + b = b - a + return a +\end{lstlisting} +\begin{lstlisting} +$ python gcd.py +All tests passed! +\end{lstlisting} %$ + \begin{itemize} + \item \alert{Success!} + \end{itemize} +\end{frame} + + +\begin{frame}[fragile] + \frametitle{Euclidean Algorithm -- Modulo} + \begin{itemize} + \item Repeated subtraction can be replaced by a modulo + \item modulo of \texttt{a\%b} is always less than b + \item when \texttt{a < b}, \texttt{a\%b} equals \texttt{a} + \item Combine these two observations, and modify the code +\begin{lstlisting} +def gcd(a, b): + while b != 0: + a, b = b, a % b + return a +\end{lstlisting} + \item Check that the tests pass again + \end{itemize} +\end{frame} + +\begin{frame}[fragile] + \frametitle{Euclidean Algorithm -- Recursive} + \begin{itemize} + \item Final improvement -- make \texttt{gcd} recursive + \item More readable and easier to understand +\begin{lstlisting} +def gcd(a, b): + if b == 0: + return a + return gcd(b, a%b) +\end{lstlisting} + \item Check that the tests pass again + \end{itemize} +\end{frame} + +\begin{frame}[fragile] + \frametitle{Document \texttt{gcd}} + \begin{itemize} + \item Undocumented function is as good as unusable + \item Let's add a docstring \& We have our first test! + \end{itemize} +\begin{lstlisting} +def gcd(a, b): + """Returns the Greatest Common Divisor of the + two integers passed as arguments. + + Args: + a: an integer + b: another integer + + Returns: Greatest Common Divisor of a and b + """ + if b == 0: + return a + return gcd(b, a%b) +\end{lstlisting} +\end{frame} + + +\begin{frame}[fragile] + \frametitle{Persistent Test Cases} + \begin{itemize} + \item Tests should be pre-determined and written, before the code + \item The file shall have multiple lines of test data + \item Separates the code from the tests + \end{itemize} +\end{frame} + +\begin{frame}[fragile] + \frametitle{Separate \texttt{test\_gcd.py}} +\begin{lstlisting} +from gcd import gcd + +def test_gcd(): + assert gcd(48, 64) == 16 + assert gcd(44, 19) == 1 + +if __name__ == '__main__': + test_gcd() +\end{lstlisting} +\end{frame} + +\section{Python Testing Frameworks} + +\begin{frame}[fragile] + \frametitle{Python Testing Frameworks} + \begin{itemize} + \item Testing frameworks essentially, ease the job of the user + \item Python provides two frameworks for testing code + \begin{itemize} + \item \texttt{unittest} framework + \item \texttt{doctest} module + \end{itemize} +\item \texttt{nose} is a package to help test + \end{itemize} +\end{frame} + +\subsection{\texttt{nose}} + +\begin{frame}[fragile] + \frametitle{\texttt{nose} tests} + \begin{itemize} + \item It is not easy to organize, choose and run tests scattered + across multiple files. + \item \texttt{nose} module aggregate these tests automatically + \item Can aggregate \texttt{unittests} and \texttt{doctests} + \item Allows us to pick and choose which tests to run + \item Helps output the test-results and aggregate them in various + formats + \item Not part of the Python distribution itself +\begin{lstlisting} +$ apt-get install python-nose +\end{lstlisting} %$ + \item Run the following command in the top level directory +\begin{lstlisting} +$ nosetests +\end{lstlisting} %$ + \end{itemize} +\end{frame} + +\end{document} |