LECTURE 14: DESIGN FOR TESTING CSE 442 Software Engineering - - PowerPoint PPT Presentation

lecture 14 design for testing
SMART_READER_LITE
LIVE PREVIEW

LECTURE 14: DESIGN FOR TESTING CSE 442 Software Engineering - - PowerPoint PPT Presentation

LECTURE 14: DESIGN FOR TESTING CSE 442 Software Engineering Easiest Code to Test Easiest Code to Test Easiest Code to Test Functional Languages Coding in Most Languages Side-effects common & makes testing more difficult


slide-1
SLIDE 1

LECTURE 14: DESIGN FOR TESTING

CSE 442 – Software Engineering

slide-2
SLIDE 2

Easiest Code to Test

slide-3
SLIDE 3

Easiest Code to Test

slide-4
SLIDE 4

Easiest Code to Test

Functional Languages

slide-5
SLIDE 5

Coding in Most Languages

¨ Side-effects common & makes testing more difficult

slide-6
SLIDE 6

Suggestions for Tests

public class Time { private int second, minute, hour; // Assume toString() also exists public void advanceTime(int secs) { second = second + secs; minute = minute + (second / 60); second = second % 60; hour = hour + (minute / 60); minute = minute % 60; while (hour > 12) { hour = hour – 12; } } }

slide-7
SLIDE 7

Suggestions for Tests

public class Time { private int second, minute, hour; // Assume toString() & getters for fields also exist public void advanceTime(int secs) { second = second + secs; minute = minute + (second / 60); second = second % 60; hour = hour + (minute / 60); minute = minute % 60; while (hour > 12) { hour = hour – 12; } } }

slide-8
SLIDE 8

Tests Access Inputs & Outputs

public class Time { private int second, minute, hour; // Assume toString() & getters for fields also exist public void advanceTime(int secs) { second = second + secs; minute = minute + (second / 60); second = second % 60; hour = hour + (minute / 60); minute = minute % 60; while (hour > 12) { hour = hour – 12; } } }

slide-9
SLIDE 9

Important Test Concept

¨ Errors likely in calculations, especially if complex

¤ Entire goal is finding & HELPING FIX bugs using tests ¤ Needs simple code that can be understood & examined ¤ Also requires easy way to set inputs & see outputs

¨ Do best to separate calculations & state changes

¤ Calculations long & complex needing lots of testing ¤ Need to be focus of testing & results important to test ¤ State changes simple & unlikely to be bug source

slide-10
SLIDE 10

Important Test Concept #2

¨ Large calculations complex & tests tough to plan

How could anyone climb that?

slide-11
SLIDE 11

Important Test Concept #2

¨ Decompose calculations into smallest pieces

¤ Smaller functions easier to read, test, & debug ¤ Test each function separately to make certain they work ¤ Once pieces done, write function combining results

slide-12
SLIDE 12

Decompose This Code

public class Time { private int second, minute, hour; public void advanceTime(int secs) { second = second + secs; minute = minute + (second / 60); second = second % 60; hour = hour + (minute / 60); minute = minute % 60; while (hour > 12) { hour = hour – 12; } } }

slide-13
SLIDE 13

Decomposed For Testing!

public class Time { private int second, minute, hour; public int advanceSeconds(int secs) { second = second + secs; advanceMinutes(second / 60); second = second % 60; return second; } public int advanceMinutes(int mins) { minute = minute + mins; advanceHours(minute / 60); minute = minute % 60; return minute; } public int advanceHours(int hrs) { hour = hour + hrs; while (hour > 12) { hour = hour – 12; } return hour; } }

slide-14
SLIDE 14

Define Modules Carefully

slide-15
SLIDE 15

Define Modules Carefully

Write code so

important calculations testable

slide-16
SLIDE 16

Not Just For Assignments

¨ Also important to separate I/O from calculations

¤ I/O tough to test, since requires reviewing output ¤ But, since built into language, even harder to fix

¨ Write functions returning Strings or bytes with data

¤ Second set of functions take in data and just output it ¤ But this can create performance issues & other problems ¤ Solution relies on understanding another issue

slide-17
SLIDE 17

Dependency Management

¨ Want to keep coupling between classes loose ¨ Dependency needed to preserve single responsibility

public class Engine { /* Code here */ } public class Car { private Engine motor; public Car() { motor = new Engine(); } /* Even more code here */ }

slide-18
SLIDE 18

Why Could drive() Fail?

public class Engine { /* Code here */ } public class Car { private Engine motor; public Car() { motor = new Engine(); } public int drive(int distance) { int gasUsed = motor.move(distance); return gasUsed; } /* Even more code here */ }

slide-19
SLIDE 19

Dependency Inversion

slide-20
SLIDE 20

Dependency Inversion

¨ Make classes advertise their dependencies

¤ Do this by adding parameters within constructor ¤ Improves testability by allowing other options ¤ Makes implementations easier when Null is an object

public class Engine { /* Code here */ } public class Car { private Engine motor; public Car() { motor = new Engine(); } /* Even more code here */ }

slide-21
SLIDE 21

Dependency Inversion

¨ Make classes advertise their dependencies

¤ Do this by adding parameters within constructor ¤ Improves testability by allowing other options ¤ Makes implementations easier when Null is an object

public class Engine { /* Code here */ } public class Car { private Engine motor; public Car(Engine inMotor) { motor = inMotor; } /* Even more code here */ }

slide-22
SLIDE 22

Tests

Dependency Inversion

¨ Dependency inversion enables using stubs & mocks ¨ Stub object fakes data to allow code to be tested

¤ Important when actual data uncontrollable or not coded:

Internet traffic Database queries File I/O Multithreaded interactions

slide-23
SLIDE 23

Where Stub Needed

public class NuclearPowerPlant { private NuclearReactor reactor; public NuclearPowerPlant() { reactor = new NuclearReactor("SNPP"); } public boolean checkForBreach() { if (!reactor.withinLimits()) return reactor.alarmSounding(); return false; } }

slide-24
SLIDE 24

Passed Test?

slide-25
SLIDE 25

Adding Dependency Inversion

public class NuclearPowerPlant { private NuclearReactor reactor; public NuclearPowerPlant(NuclearReactor n){ reactor = n; } public boolean checkForBreach() { if (!reactor.withinLimits()) return reactor.alarmSounding(); return false; } }

slide-26
SLIDE 26

Create Interface For Stub

public interface NR { public boolean withinLimits(); public boolean alarmSounding(); } public class NuclearPowerPlant { private NR reactor; public NuclearPowerPlant(NR n){ reactor = n; } public boolean checkForBreach() { if (!reactor.withinLimits()) return reactor.alarmSounding(); return false; } }

slide-27
SLIDE 27

Why We Write Stubs

Testing NuclearPowerPlant

NOT

NuclearReactor

slide-28
SLIDE 28

Create Stub Class

¨ Only write code test needs – only reason stub exists

¤ Code for NuclearReactor developed separately

¨ ONLY purpose is testing other classes code

¤ Often hard-code values; make it as simple as possible ¤ Stub used to simplify; avoid using files, networks, etc.

¨ Use of duct tape & stubs similar

¤ Not for serious fix, but useful in a pinch

slide-29
SLIDE 29

Writing Stub Class

public interface NR { public boolean withinLimits(); public boolean alarmSounding(); } public class MeltdownStub implements NR{ public boolean withinLimits() { return false; } public boolean alarmSounding() { return true; } }

slide-30
SLIDE 30

Using Stubs

¨ Can now run class in many different ways

¤ Constructor passed stub instance to test code works ¤ Actual reactor instance in production to avoid warnings

¨ If more tests desired, can create additional stubs

¤ Easy to write stub, since all of the data is hard-coded ¤ Tests must be convincing & errors not due to test code

slide-31
SLIDE 31

Using Stubs

¨ Can now run class in many different ways

¤ Constructor passed stub instance to test code works ¤ Actual reactor instance in production to avoid warnings

¨ If more tests desired, can create additional stubs

¤ Easy to write stub, since all of the data is hard-coded ¤ Tests must be convincing & errors not due to test code

  • r
slide-32
SLIDE 32

Using Stub Class

public class MeltdownStub implements NR { public boolean withinLimits() { return false; } public boolean alarmSounding() { return true; } } public class NuclearPowerPlant { public boolean checkForBreach() { if (!reactor.withinLimits()) return reactor.alarmSounding(); return false; }} @Test public void testTMI() { NR ms = new MeltdownStub(); NuclearPowerPlant tmi=new NuclearPowerPlant(ms); assertTrue(tmi.checkForBreach()); }

slide-33
SLIDE 33

Stub Class Review

¨ Define interface so hard-to-use classes have options

¤ Interface defines minimum number of methods ¤ Update existing class to implement this interface

¨ Requires using dependency inversion in holding class

¤ Pass instance to constructor & eliminate new command ¤ Not a bad idea, in general, since also improves coupling

¨ Stub(s) used in test(s); actual class in production

¤ Easy to create many stubs, since often provide constant ¤ Testing for hard situations or while waiting on others

slide-34
SLIDE 34

Tests

Dependency Inversion

¨ Dependency inversion enables using stubs & mocks ¨ Mock object tracks calls to test class interactions

¤ Important when important to check call or arguments ¤ Checks class interactions and not method results ¤ Test case uses results in mock object to see if passing

slide-35
SLIDE 35

Where Mock Needed

public class EmergencySystem { private AlertReport reporter; public EmergencySystem(AlertReport r) { reporter = r; } public void incomingMissle() { reporter.sendEASAlert(); } }

slide-36
SLIDE 36

Passed Test?

slide-37
SLIDE 37

Using Mock Class

public class MockAlert implements AlertReport { public boolean alertSent = false; public boolean sendEASAlert() {alertSent = true;}} public class EmergencySystem {

public void incomingMissle() { reporter.sendEASAlert(); } }

@Test public void testNOTHawaii() { AlertReport ma = new MockAlert(); EmergencySystem eas=new EmergencySystem(ma); eas.incomingMissle(); assertTrue(ma.alertSent); }

slide-38
SLIDE 38

Why Mock Classes Help

¨ Define interface so hard-to-test interactions testable

¤ Interface defines minimum number of methods ¤ Update existing class to implement this interface

¨ Requires using dependency inversion in holding class

¤ Pass instance to constructor & eliminate new command ¤ Not a bad idea, in general, since also improves coupling

¨ Mock(s) used in test(s); actual class in production

¤ May not need many mocks, since looking at interaction ¤ Combines well with stubs to force events to occur

slide-39
SLIDE 39

Try On Your Own

¨ Design TESTABLE function(s) to solve this problem:

Each day, the Haas Avocado Board posts average price per avocado & total avocados sold for each of their US markets. The data are posted as a CSV file and always at the same URL. Your code updates a webpage to show the average price per avocado for the US, total US sales, and the price & sales data for the 4 NY markets (Buffalo, NYC, Albany, Syracuse) for each day in the file

slide-40
SLIDE 40

My Solution

get_data(url)→ 2d array with file's data get_us_sold(array, day)→ #sold that day get_us_income(array, day)→ $sold that day get_us_avg_price(array, day)→ (obvs) get_market_sold(array, name, day)→ format_us_results(day, sold, price) → return HTML table with the us results for that day