Added jdblischak's testing content from 2013-06-chicago
authorWill Trimble <trimble@anl.gov>
Wed, 31 Jul 2013 20:53:42 +0000 (16:53 -0400)
committerW. Trevor King <wking@tremily.us>
Sat, 2 Nov 2013 15:18:14 +0000 (08:18 -0700)
W. Trevor King: The provenance for this commit is unclear from the Git
history alone, so I asked Will directly about Example2dnasequences.md.
He responded with:

On Fri, Nov 01, 2013 at 06:31:27PM -0400, Will Trimble wrote:
> The file you ask about by name is the tail end of the file
> https://github.com/jdblischak/boot-camps/blob/2013-06-chicago/09-testing/Readme.md
> It looks like it was written 2013-06-13.
>
> It was a perhaps-misguided attempt to factorize the Fibionacci example from
> the bio-inflected example of John's.
>
> This half-module on TDD exists in addition to three other edits of the
> testing module:  https://github.com/swcarpentry/bc/issues/106  (on
> ecological, one basic stats, one Fibonacci that John replaced)

1  2 
python/testing/Example2dnasequences.md
python/testing/Readme.md
python/testing/example2/calculate_gc.py
python/testing/example2/mean.py
python/testing/example2/test_calculate_gc.py
python/testing/example2/test_mean.py
python/testing/example2/test_transcribe.py
python/testing/example2/transcribe.py

index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..661b93e94ab9d9d55063c75ef0d2cf9be5cc96b7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,95 @@@
++**Materials originally by John Blischak**
++
++## A TDD Example
++
++To illustrate TDD, let's return to the function you wrote yesterday,
++`calculate_gc`. We'll start from scratch and develop the function
++by meeting test specifications. 
++
++The beginning of the function is contained in the file `calculate_gc.py`
++in this directory. It currently takes one argument as input, but does
++nothing.
++
++```python
++def calculate_gc(x):
++    '''
++    Calculates the GC content of DNA sequence x.
++    '''
++    pass
++```
++
++The tests that we must pass are contained in the file
++`test_calculate_gc.py`. We can run the tests using `nosetests`.
++
++    nosetests -v test_calculate_gc.py
++
++As expected, we fail all the tests! What is the bare minimum 
++functionality we must add to pass the first test below?
++
++```python
++def test_only_G_and_C():
++    '''
++    Sequence of only G's and C's has fraction 1.0
++    '''
++    fixture = 'GGCGCCGGC'
++    result = calculate_gc(fixture)
++    assert_equal(result, 1.0)
++```
++
++And the second test?
++
++```python
++def test_half():
++    '''
++    Sequence with half G and C has fraction 0.5
++    '''
++    fixture = 'ATGC'
++    result = calculate_gc(fixture)
++    assert_equal(result, 0.5)
++```
++
++Test number three?
++
++```python
++def test_lower_case():
++    '''
++    Sequence with lower case letters
++    '''
++    fixture = 'atgc'
++    result = calculate_gc(fixture)
++    assert_equal(result, 0.5)
++```
++
++Test number four?
++
++```python
++def test_not_DNA():
++    '''
++    Raise TypeError if not DNA
++    '''
++    fixture = 'qwerty'
++    assert_raises(TypeError, calculate_gc, fixture)
++```
++
++Through this cycle of writing tests and modifying the function to pass 
++the tests, we have developed a function that behaves exactly as we 
++expect and nothing more. And the tests not only serve as documentation 
++of what the function does, but can also be easily ran again if we made 
++further modifications (regression tests). What would be the next test 
++you would write for our function?
++
++## Exercise: Test function that transcribes DNA to RNA
++
++In the lesson yesterday on functions, `05-python-functions`, one exercise
++asked you to write a function to transcribe DNA to RNA. An example of
++that function is implemented in this directory in a file named
++`transcribe.py`. In that lesson, there were two tests to check your
++work:
++
++```python
++transcribe('ATGC') == 'UACG'
++transcribe('ATGCAGTCAGTGCAGTCAGT') == 'UACGUCAGUCACGUCAGUCA'
++```
++
++Convert these to a proper test and place it the file `test_transcribe.py`.
++Next, add a few tests of your own and run the test suite with nosetests.
index 8d53b87fd71e4cb0d03b3be67d3b7ab41797d221,a23e081fe2c883ab59723e3ef72050433bd2b909..a6f9985a372f285c50ea4638af8116cf535456d6
@@@ -50,7 -51,7 +50,7 @@@ answers we are interested in, data we w
  *Uncertainty Quantification* is the process of asking, "Given that our
  algorithm may not be deterministic, was our execution within acceptable
  error bounds?" This is particularly important for anything which uses
--random numbers, eg Monte Carlo methods.
++random numbers, for example, Monte Carlo methods.
  
  # Where are tests?
  
@@@ -276,11 -275,11 +276,11 @@@ teardown(
  
  # Nose: A Python Testing Framework
  
 -The testing framework we'll discuss today is called
 -[nose](https://nose.readthedocs.org/en/latest/). However, there are
 -several other testing frameworks available in most languages. Most
 +The testing framework we'll discuss today is called nose. However, there
- are several other testing frameworks available in most language. Most
++are several other testing frameworks available in most languages. Most
  notably there is [JUnit](http://www.junit.org/) in Java which can
  arguably attributed to inventing the testing framework.
 +
  ## Where do nose tests live?
  
  Nose tests are files that begin with `Test-`, `Test_`, `test-`, or
@@@ -359,192 -358,94 +359,192 @@@ style was put forth most strongly by [K
  
  ## A TDD Example
  
 -To illustrate TDD, let's return to the function you wrote yesterday,
 -`calculate_gc`. We'll start from scratch and develop the function
 -by meeting test specifications. 
 +Say you want to write a fib() function which generates values of the
 +Fibonacci sequence of given indexes. You would - of course - start by
 +writing the test, possibly testing a single value:
 +
 +```python
 +from nose.tools import assert_equal
 +
 +from pisa import fib
  
 -The beginning of the function is contained in the file `calculate_gc.py`
 -in this directory. It currently takes one argument as input, but does
 -nothing.
 +def test_fib1():
 +    obs = fib(2)
 +    exp = 1
 +    assert_equal(obs, exp)
 +```
 +
 +You would *then* go ahead and write the actual function:
  
  ```python
 -def calculate_gc(x):
 -    '''
 -    Calculates the GC content of DNA sequence x.
 -    '''
 -    pass
 +def fib(n):
 +    # you snarky so-and-so
 +    return 1
  ```
  
 -The tests that we must pass are contained in the file
 -`test_calculate_gc.py`. We can run the tests using `nosetests`.
 +And that is it right?! Well, not quite. This implementation fails for
 +most other values. Adding tests we see that:
  
 -    nosetests -v test_calculate_gc.py
 +```python
 +def test_fib1():
 +    obs = fib(2)
 +    exp = 1
 +    assert_equal(obs, exp)
  
 -As expected, we fail all the tests! What is the bare minimum 
 -functionality we must add to pass the first test below?
 +
 +def test_fib2():
 +    obs = fib(0)
 +    exp = 0
 +    assert_equal(obs, exp)
 +
 +    obs = fib(1)
 +    exp = 1
 +    assert_equal(obs, exp)
 +```
 +
 +This extra test now requires that we bother to implement at least the
 +initial values:
  
  ```python
 -def test_only_G_and_C():
 -    '''
 -    Sequence of only G's and C's has fraction 1.0
 -    '''
 -    fixture = 'GGCGCCGGC'
 -    result = calculate_gc(fixture)
 -    assert_equal(result, 1.0)
 +def fib(n):
 +    # a little better
 +    if n == 0 or n == 1:
 +        return n
 +    return 1
 +```
 +
 +However, this function still falls over for `2 < n`. Time for more
 +tests!
 +
 +```python
 +def test_fib1():
 +    obs = fib(2)
 +    exp = 1
 +    assert_equal(obs, exp)
 +
 +
 +def test_fib2():
 +    obs = fib(0)
 +    exp = 0
 +    assert_equal(obs, exp)
 +
 +    obs = fib(1)
 +    exp = 1
 +    assert_equal(obs, exp)
 +
 +
 +def test_fib3():
 +    obs = fib(3)
 +    exp = 2
 +    assert_equal(obs, exp)
 +
 +    obs = fib(6)
 +    exp = 8
 +    assert_equal(obs, exp)
  ```
  
 -And the second test?
 +At this point, we had better go ahead and try do the right thing...
  
  ```python
 -def test_half():
 -    '''
 -    Sequence with half G and C has fraction 0.5
 -    '''
 -    fixture = 'ATGC'
 -    result = calculate_gc(fixture)
 -    assert_equal(result, 0.5)
 +def fib(n):
 +    # finally, some math
 +    if n == 0 or n == 1:
 +        return n
 +    else:
 +        return fib(n - 1) + fib(n - 2)
  ```
  
 -Test number three?
 +Here it becomes very tempting to take an extended coffee break or
 +possibly a power lunch. But then you remember those pesky negative
 +numbers and floats. Perhaps the right thing to do here is to just be
 +undefined.
  
  ```python
 -def test_lower_case():
 -    '''
 -    Sequence with lower case letters
 -    '''
 -    fixture = 'atgc'
 -    result = calculate_gc(fixture)
 -    assert_equal(result, 0.5)
 +def test_fib1():
 +    obs = fib(2)
 +    exp = 1
 +    assert_equal(obs, exp)
 +
 +
 +def test_fib2():
 +    obs = fib(0)
 +    exp = 0
 +    assert_equal(obs, exp)
 +
 +    obs = fib(1)
 +    exp = 1
 +    assert_equal(obs, exp)
 +
 +
 +def test_fib3():
 +    obs = fib(3)
 +    exp = 2
 +    assert_equal(obs, exp)
 +
 +    obs = fib(6)
 +    exp = 8
 +    assert_equal(obs, exp)
 +
 +
 +def test_fib3():
 +    obs = fib(13.37)
 +    exp = NotImplemented
 +    assert_equal(obs, exp)
 +
 +    obs = fib(-9000)
 +    exp = NotImplemented
 +    assert_equal(obs, exp)
  ```
  
 -Test number four?
 +This means that it is time to add the appropriate case to the function
 +itself:
  
  ```python
 -def test_not_DNA():
 -    '''
 -    Raise TypeError if not DNA
 -    '''
 -    fixture = 'qwerty'
 -    assert_raises(TypeError, calculate_gc, fixture)
 +def fib(n):
 +    # sequence and you shall find
 +    if n < 0 or int(n) != n:
 +        return NotImplemented
 +    elif n == 0 or n == 1:
 +        return n
 +    else:
 +        return fib(n - 1) + fib(n - 2)
  ```
  
 -Through this cycle of writing tests and modifying the function to pass 
 -the tests, we have developed a function that behaves exactly as we 
 -expect and nothing more. And the tests not only serve as documentation 
 -of what the function does, but can also be easily ran again if we made 
 -further modifications (regression tests). What would be the next test 
 -you would write for our function?
 +# Quality Assurance Exercise
 +
- Can you think of other tests to make for the fibonacci function? I promise there 
++Can you think of other tests to make for the Fibonacci function? I promise there 
 +are at least two. 
 +
 +Implement one new test in test_fib.py, run nosetests, and if it fails, implement 
 +a more robust function for that case.
 +
 +And thus - finally - we have a robust function together with working
 +tests!
  
 -## Exercise: Test function that transcribes DNA to RNA
 +# Exercise
  
 -In the lesson yesterday on functions, `05-python-functions`, one exercise
 -asked you to write a function to transcribe DNA to RNA. An example of
 -that function is implemented in this directory in a file named
 -`transcribe.py`. In that lesson, there were two tests to check your
 -work:
 +**The Problem:** In 2D or 3D, we have two points (p1 and p2) which
 +define a line segment. Additionally there exists experimental data which
 +can be anywhere in the domain. Find the data point which is closest to
 +the line segment.
 +
 +In the `close_line.py` file there are four different implementations
 +which all solve this problem. [You can read more about them
 +here.](http://inscight.org/2012/03/31/evolution_of_a_solution/) However,
 +there are no tests! Please write from scratch a `test_close_line.py`
 +file which tests the closest\_data\_to\_line() functions.
 +
 +*Hint:* you can use one implementation function to test another. Below
 +is some sample data to help you get started.
 +
 +![image](https://github.com/thehackerwithin/UofCSCBC2012/raw/scopz/5-Testing/evo_sol1.png)
 +> -
  
  ```python
 -transcribe('ATGC') == 'UACG'
 -transcribe('ATGCAGTCAGTGCAGTCAGT') == 'UACGUCAGUCACGUCAGUCA'
 +import numpy as np
 +
 +p1 = np.array([0.0, 0.0])
 +p2 = np.array([1.0, 1.0])
 +data = np.array([[0.3, 0.6], [0.25, 0.5], [1.0, 0.75]])
  ```
  
 -Convert these to a proper test and place it the file `test_transcribe.py`.
 -Next, add a few tests of your own and run the test suite with nosetests.
index 0000000000000000000000000000000000000000,1838d4e3aa800224ccb00918bdc2302462171943..1838d4e3aa800224ccb00918bdc2302462171943
mode 000000,100644..100644
--- /dev/null
index 0000000000000000000000000000000000000000,7b419d6e1e869080a99431fa87c917a7ba0fb977..7b419d6e1e869080a99431fa87c917a7ba0fb977
mode 000000,100644..100644
--- /dev/null
index 0000000000000000000000000000000000000000,b2707353c48e8ba86f25043a4d1f6100fca32916..b2707353c48e8ba86f25043a4d1f6100fca32916
mode 000000,100644..100644
--- /dev/null
index 0000000000000000000000000000000000000000,44c3b7c6999e9e79e9dbcbb002bdfd3b735ac66e..44c3b7c6999e9e79e9dbcbb002bdfd3b735ac66e
mode 000000,100644..100644
--- /dev/null
index 0000000000000000000000000000000000000000,d9ac27faef4f8ad11144b408ba3c2de85bb3d953..d9ac27faef4f8ad11144b408ba3c2de85bb3d953
mode 000000,100644..100644
--- /dev/null
index 0000000000000000000000000000000000000000,488b9b75aeb806169c0c8bfa4ed94f4613b0b736..488b9b75aeb806169c0c8bfa4ed94f4613b0b736
mode 000000,100644..100644
--- /dev/null