19
June 2023, 2016 | Berlin, Germany

Summit 16: Stop Writing Legacy Code!

  • Upload
    opnfv

  • View
    250

  • Download
    0

Embed Size (px)

Citation preview

June 20–23, 2016 | Berlin, Germany

Stop Writing Legacy CodeMark Beierl, EMC

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

Stop Writing Legacy CodeMark Beierl, EMC