70
Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution Lice See http://software-carpentry.org/license.html for more informati Testing

Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Embed Size (px)

Citation preview

Page 1: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Interface and Implementation

Copyright © Software Carpentry 2010

This work is licensed under the Creative Commons Attribution License

See http://software-carpentry.org/license.html for more information.

Testing

Page 2: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

One of the most important ideas in computing is

the difference between interface and implementation

Page 3: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

One of the most important ideas in computing is

the difference between interface and implementation

Interface: how something interacts with the world

Page 4: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

One of the most important ideas in computing is

the difference between interface and implementation

Interface: how something interacts with the world

Implementation: how it does what it does

Page 5: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

One of the most important ideas in computing is

the difference between interface and implementation

Interface: how something interacts with the world

Implementation: how it does what it does

def integrate(func, x1, x2):

...math goes here...

return result

Page 6: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

One of the most important ideas in computing is

the difference between interface and implementation

Interface: how something interacts with the world

Implementation: how it does what it does

def integrate(func, x1, x2):

...math goes here...

return result

Interface: (f, x1, x2) -> integral

Page 7: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

One of the most important ideas in computing is

the difference between interface and implementation

Interface: how something interacts with the world

Implementation: how it does what it does

def integrate(func, x1, x2):

...math goes here...

return result

Interface: (f, x1, x2) -> integral

Implementation: we don't (have to) care

Page 8: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Often use this idea to simplify unit testing

Page 9: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Often use this idea to simplify unit testing

Want to test components in program one by one

Page 10: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Often use this idea to simplify unit testing

Want to test components in program one by one

But components depend on each other

Page 11: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Often use this idea to simplify unit testing

Want to test components in program one by one

But components depend on each other

How to isolate the component under test from

other components?

Page 12: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Often use this idea to simplify unit testing

Want to test components in program one by one

But components depend on each other

How to isolate the component under test from

other components?

Replace the other components with things that have

the same interfaces, but simpler implementations

Page 13: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Often use this idea to simplify unit testing

Want to test components in program one by one

But components depend on each other

How to isolate the component under test from

other components?

Replace the other components with things that have

the same interfaces, but simpler implementations

Sometimes requires refactoring

Page 14: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Often use this idea to simplify unit testing

Want to test components in program one by one

But components depend on each other

How to isolate the component under test from

other components?

Replace the other components with things that have

the same interfaces, but simpler implementations

Sometimes requires refactoring

Or some up-front design

Page 15: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Back to those fields in Saskatchewan...

Page 16: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Test function that reads a photo from file

Page 17: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Test function that reads a photo from file

def read_photo(filename):

result = set()

reader = open(filename, 'r')

…fill result with rectangles in file…

reader.close()

return result

Page 18: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Test function that reads a photo from file

def read_photo(filename):

result = set()

reader = open(filename, 'r')

…fill result with rectangles in file…

reader.close()

return result

def test_photo_containing_only_unit():

assert read_photo('unit.pht') == { ((0, 0), (1, 1)) }

Page 19: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Experience teaches that this is a bad idea

Page 20: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Experience teaches that this is a bad idea

1. External files can easily be misplaced

Page 21: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Experience teaches that this is a bad idea

1. External files can easily be misplaced

2. Hard to understand test if fixture stored elsewhere

Page 22: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Experience teaches that this is a bad idea

1. External files can easily be misplaced

2. Hard to understand test if fixture stored elsewhere

3. File I/O is much slower than memory operations

Page 23: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Experience teaches that this is a bad idea

1. External files can easily be misplaced

2. Hard to understand test if fixture stored elsewhere

3. File I/O is much slower than memory operations

The longer tests take to run, the less often they

will be run

Page 24: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Experience teaches that this is a bad idea

1. External files can easily be misplaced

2. Hard to understand test if fixture stored elsewhere

3. File I/O is much slower than memory operations

The longer tests take to run, the less often they

will be run

And the more often developers will have to backtrack

to find and fix bugs

Page 25: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Original function

def count_rect(filename):

reader = open(filename, 'r')

count = 0

for line in reader:

count += 1

reader.close()

return count

Page 26: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Original function

One rectangle per line, no comments or blank lines

def count_rect(filename):

reader = open(filename, 'r')

count = 0

for line in reader:

count += 1

reader.close()

return count

Page 27: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Original function

One rectangle per line, no comments or blank lines

Real counter would be more sophisticated

def count_rect(filename):

reader = open(filename, 'r')

count = 0

for line in reader:

count += 1

reader.close()

return count

Page 28: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Refactored

def count_rect_in(reader):

count = 0

for line in reader:

count += 1

return count

def count_rect(filename):

reader = open(filename, 'r')

result = count_rect_in(reader)

reader.close()

return result

Page 29: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Refactored

Does the work, but

does not open the file

def count_rect_in(reader):

count = 0

for line in reader:

count += 1

return count

def count_rect(filename):

reader = open(filename, 'r')

result = count_rect_in(reader)

reader.close()

return result

Page 30: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Refactored

Opens the file

def count_rect_in(reader):

count = 0

for line in reader:

count += 1

return count

def count_rect(filename):

reader = open(filename, 'r')

result = count_rect_in(reader)

reader.close()

return result

Page 31: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Refactored

Opens the file

Keeps name of

original function

def count_rect_in(reader):

count = 0

for line in reader:

count += 1

return count

def count_rect(filename):

reader = open(filename, 'r')

result = count_rect_in(reader)

reader.close()

return result

Page 32: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Now write tests

Page 33: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Now write tests

from StringIO import StringIO

Data = '''0 0 1 1

1 0 2 1

2 0 3 1'''

def test_num_rect():

reader = StringIO(Data)

assert count_rect(reader) == 3

Page 34: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Now write tests

A "file" that tests

can be run on

from StringIO import StringIO

Data = '''0 0 1 1

1 0 2 1

2 0 3 1'''

def test_num_rect():

reader = StringIO(Data)

assert count_rect(reader) == 3

Page 35: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Now write tests

Acts like a file, but

uses a string in

memory for storage

from StringIO import StringIO

Data = '''0 0 1 1

1 0 2 1

2 0 3 1'''

def test_num_rect():

reader = StringIO(Data)

assert count_rect(reader) == 3

Page 36: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Now write tests

Doesn't know it

isn't reading from

a real file

from StringIO import StringIO

Data = '''0 0 1 1

1 0 2 1

2 0 3 1'''

def test_num_rect():

reader = StringIO(Data)

assert count_rect(reader) == 3

Page 37: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Use the same method to test output

Page 38: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Use the same method to test output

Write to a StringIO

Page 39: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Use the same method to test output

Write to a StringIO

Use getvalue to get and check its final contents

Page 40: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

def test_write_unit_only():

fixture = { ((0, 0), (1, 1)) }

writer = StringIO()

photo_write(fixture, writer)

result = writer.getvalue()

assert result == '0 0 1 1\n'

Use the same method to test output

Write to a StringIO

Use getvalue to get and check its final contents

Page 41: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Use the same method to test output

Write to a StringIO

Use getvalue to get and check its final contents

Doesn't know it

isn't reading from

a real file

def test_write_unit_only():

fixture = { ((0, 0), (1, 1)) }

writer = StringIO()

photo_write(fixture, writer)

result = writer.getvalue()

assert result == '0 0 1 1\n'

Page 42: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Use the same method to test output

Write to a StringIO

Use getvalue to get and check its final contents

Get everything

written to the

StringIO as a string

def test_write_unit_only():

fixture = { ((0, 0), (1, 1)) }

writer = StringIO()

photo_write(fixture, writer)

result = writer.getvalue()

assert result == '0 0 1 1\n'

Page 43: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

One more task

Page 44: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

def photo_write(photo, writer):

contents = list(photo)

contents.sort()

for rect in contents:

print >> writer, rect[0][0], rect[0][1],

rect[1][0], rect[1][1]

One more task

Page 45: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

One more task

Why do the extra work of sorting?

def photo_write(photo, writer):

contents = list(photo)

contents.sort()

for rect in contents:

print >> writer, rect[0][0], rect[0][1],

rect[1][0], rect[1][1]

Page 46: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

One more task

Why do the extra work of sorting?

def photo_write(photo, writer):

contents = list(photo)

contents.sort()

for rect in contents:

print >> writer, rect[0][0], rect[0][1],

rect[1][0], rect[1][1]

Page 47: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

def photo_write(photo, writer):

for rect in photo:

print >> writer, rect[0][0], rect[0][1],

rect[1][0], rect[1][1]

This version is simpler and faster

Page 48: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

This version is simpler and faster

But there is no way to predict its output!

def photo_write(photo, writer):

for rect in photo:

print >> writer, rect[0][0], rect[0][1],

rect[1][0], rect[1][1]

Page 49: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Page 50: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

two_fields = { ((0, 0), (1, 1)), ((1, 0), (2, 1)) }

photo_write(two_fields, …)

Page 51: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

two_fields = { ((0, 0), (1, 1)), ((1, 0), (2, 1)) }

photo_write(two_fields, …)

0 0 1 1

1 0 2 1

Page 52: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

two_fields = { ((0, 0), (1, 1)), ((1, 0), (2, 1)) }

photo_write(two_fields, …)

0 0 1 1

1 0 2 1

1 0 2 1

0 0 1 1

Page 53: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

two_fields = { ((0, 0), (1, 1)), ((1, 0), (2, 1)) }

photo_write(two_fields, …)

0 0 1 1

1 0 2 1

1 0 2 1

0 0 1 1≠

Page 54: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

two_fields = { ((0, 0), (1, 1)), ((1, 0), (2, 1)) }

photo_write(two_fields, …)

0 0 1 1

1 0 2 1

1 0 2 1

0 0 1 1≠

Sets are unordered

Page 55: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

two_fields = { ((0, 0), (1, 1)), ((1, 0), (2, 1)) }

photo_write(two_fields, …)

0 0 1 1

1 0 2 1

1 0 2 1

0 0 1 1≠

Sets are unordered

Set elements are stored

in an arbitrary order

Page 56: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

two_fields = { ((0, 0), (1, 1)), ((1, 0), (2, 1)) }

photo_write(two_fields, …)

0 0 1 1

1 0 2 1

1 0 2 1

0 0 1 1≠

Sets are unordered

Set elements are stored

in an arbitrary order

We can't test if we can't predict the result

Page 57: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Our existing tests are inconsistent

Page 58: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

# From input test

Data = '''0 0 1 1

1 0 2 1

2 0 3 1'''

Our existing tests are inconsistent

Page 59: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

# From input test

Data = '''0 0 1 1

1 0 2 1

2 0 3 1'''

# From output test

def test_write_unit_only():

fixture = { ((0, 0), (1, 1)) }

assert result == '0 0 1 1\n'

Our existing tests are inconsistent

Page 60: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Our existing tests are inconsistent

# From input test

Data = '''0 0 1 1

1 0 2 1

2 0 3 1'''

# From output test

def test_write_unit_only():

fixture = { ((0, 0), (1, 1)) }

assert result == '0 0 1 1\n'

Page 61: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Our existing tests are inconsistent

Do photo files have

a newline at the end

of the last line or not?

# From input test

Data = '''0 0 1 1

1 0 2 1

2 0 3 1'''

# From output test

def test_write_unit_only():

fixture = { ((0, 0), (1, 1)) }

assert result == '0 0 1 1\n'

Page 62: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Our existing tests are inconsistent

Do photo files have

a newline at the end

of the last line or not?

Either answer is better

than "maybe"

# From input test

Data = '''0 0 1 1

1 0 2 1

2 0 3 1'''

# From output test

def test_write_unit_only():

fixture = { ((0, 0), (1, 1)) }

assert result == '0 0 1 1\n'

Page 63: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Have to design for test

Page 64: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Have to design for test

Depend on interface, not implementation

Page 65: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Have to design for test

Depend on interface, not implementation

– So it's easy to replace other components for testing

Page 66: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Have to design for test

Depend on interface, not implementation

– So it's easy to replace other components for testing

– And tests don't have to be rewritten over and over

Page 67: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Have to design for test

Depend on interface, not implementation

– So it's easy to replace other components for testing

– And tests don't have to be rewritten over and over

Isolate interactions with outside world

Page 68: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Have to design for test

Depend on interface, not implementation

– So it's easy to replace other components for testing

– And tests don't have to be rewritten over and over

Isolate interactions with outside world

– Like opening files

Page 69: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

Testing Interface and Implementation

Have to design for test

Depend on interface, not implementation

– So it's easy to replace other components for testing

– And tests don't have to be rewritten over and over

Isolate interactions with outside world

– Like opening files

Make things you are going to examine deterministic

Page 70: Interface and Implementation Copyright © Software Carpentry 2010 This work is licensed under the Creative Commons Attribution License See

August 2010

created by

Greg Wilson

Copyright © Software Carpentry 2010

This work is licensed under the Creative Commons Attribution License

See http://software-carpentry.org/license.html for more information.