Date post: | 07-Jan-2017 |
Category: |
Technology |
Upload: | opnfv |
View: | 250 times |
Download: | 0 times |
Agenda
• What is Legacy Code?
• What are Unit Tests, Really?
• How to Write Tests
• How to Deal With Existing Code
• Code Coverage
• Jenkins Reporting
3
What Is Legacy Code?
• Spaghetti code
• Poorly structured
• Not documented, or misleading comments
• Difficult to change, hard to understand
• “Someone else’s code”
• It is code without tests.
• With tests, we can change the behaviour of code quickly and verifiably
• Without them, we don’t know if our code is getting better, or worse.
4
From Working Effectively With Legacy CodeMichael C. Feathers
A Little Background
• Traditional developer from 1992 to 2008
• Worked for a company that “went agile”
• Hired an agile coach
• Worked with us daily and pointed out patterns
• Introduced “done, done, done, done”
• Introduced concept of automated testing
• No new code should be considered done without automated tests
• Concept of testing at this level was new to me
• Mocks, stubs and fakes, oh my
5
But What Is a Unit Test?
• Take a poll on the definition:
• Method level?
• If clause level?
• Success path / failure path?
• Automated, or manual set up?
• Special environment to run?
• The meaning does not match Agile practices, and does not meet the goal of avoiding legacy code
• A new term – “MicroTest”
6
So What Is a Micro Test?
• Short, few lines of code
• Always automated
• Purpose built test application
• Test a single branch of logic
• Test code written to same standard as regular code
• Test code is in git too
• Serves as gateway to commit
• Very quick
• milliseconds per test
7
From They’re Called Microtestshttp://anarchycreek.com/2009/05/20/theyre-called-microtests/
• Precise feedback on errors
• Part of a collection
• Easy to invoke
• Grey box
• Can manipulate contents if needed
• Avoids use of collaborators through the use of mock or stub objects
• Involves creation of very few objects
• Does not require any external software
Writing Tests
• What do I test?• Expected behaviour
• Logic paths
• External API
• Exceptions
• Impossible conditions
• What don’t I test?• Things that are too simple to break?
• Getters / Setters
• When have I tested enough?• When fear turns to boredom…
8
Tests as Documentation
• A good test demonstrates:• Functionality
• Expected inputs / outputs
• Exception handling
• Interactions with other objects
• Tests can serve as a document about how to use the API• Example of how to use the function under test
• What types of exceptions can happen
9
Idempotent and Independent
• Tests must:• Be self-contained
• Be repeatable
• Have everything needed to cover all logic paths
• Tests must not:• Cause changes in the environment
• Leave anything behind
• Depend on prior test execution
• Have any side effects
• Launch a missile
10
But My Function Launches a Missile
• How can I possibly test this without starting a war?
• Code for testability:• Use “fake classes” for testing
• Refactor to launcher = getMissileLauncher()
• Subclass to return fake missile launcher
• Not always practical:• File, DB, System processes, TCP sockets, etc
• Use Python Mock libraries
• Mock pretends to be real object, but does what you want
11
def launch():
launcher = new MissileLauncher()
launcher.armMissile()
launcher.fire()
Mock – Like Turtle Soup?
• Simulated objects that mimic the behavior of real objects in controlled ways
• Use a mock if the object:• Has non-deterministic results
• (e.g. the current time or the current temperature)
• has states that are difficult to create or reproduce • (e.g. a network error)
• is slow • (e.g. a complete database, which would have to be initialized before the
test)
12
From Wikipediahttps://en.wikipedia.org/wiki/Mock_object
Don’t Launch Any Missiles
• So how to test the missile launcher?
• Write the test to call the launch method
• Using @mock.patch, specify which methodsare going to be under your control
• When the mock is called, it does onlywhat it is told to do, nothing more
• Additionally, you can:• Verify that the method was called
• Verify parameters that were passed in
• Verify that other methods were not called
13
from missile import launcher
def launch():
launcher.armMissile()
launcher.fire()
@mock.patch(“launcher.fire”)
@mock.patch(“launcher.armMissile”)
def test_launch(mock1, mock2):
launch()
assertTrue(mock1.fire.called)
Reading from a Database
• Mocks can return values • execute.side_effect = mock row
• fetchone.side_effect = None
• Doesn’t matter what the SQL statement is• No DB interaction actually happened
• We control the return values
• First test, return None
• Second test, return some value
• Can control flow of logic of function from test harness
14
row = cursor.execute(“SQL…”)
if row.fetchone() is not None:
... do something
else:
... do something else
Exceptional Exceptions
• But, how do I test DB exception?
• Mocks can also force exceptions to be thrown• Any exception
• From any function
• Even supposedly impossible conditions can be programmed to occur
• No logic path or exception handler should go without testing!
15
Prove it
• Can we show off? Yes!
• Nosetests with coverage
• Cobertura• http://cobertura.github.io/cobertura/
• Jenkins code coverage• https://wiki.jenkins-ci.org/display/JENKINS/Cobertura+Plugin
• Can pass/fail right from Jenkins
• Confluence integration• Jenkins job status
16
Protect It
• Putting it all together:• Lots of very fast micro tests
• Covering a predefined percentage of the code base
• … or Jenkins will fail the job
• A perfect companion to Gerrit• Pre-review gate (the verify job)
• Reviews can be rejected if• A test is broken
• The percentage of code coverage drops
• Prevents “Legacy Code”
17
What Have We Learned?
• Code without tests is tomorrow’s legacy code
• Microtests = “Good Unit Tests”• Fast, repeatable, Idempotent, Independent
• Mocks replace slow, dangerous or difficult collaborators
• There is no code that is too complex to test
• Jenkins knows how to read unit test and code coverage results
• Gerrit can prevent patches that violate the norms set by the project
18