lecture 14 design for testing
play

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


  1. LECTURE 14: DESIGN FOR TESTING CSE 442 – Software Engineering

  2. Easiest Code to Test

  3. Easiest Code to Test

  4. Easiest Code to Test Functional Languages

  5. Coding in Most Languages ¨ Side-effects common & makes testing more difficult

  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; } } }

  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; } } }

  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; } } }

  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

  10. Important Test Concept #2 ¨ Large calculations complex & tests tough to plan How could anyone climb that?

  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

  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; } } }

  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; } }

  14. Define Modules Carefully

  15. Define Modules Carefully Write code so important calculations testable

  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

  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 */ }

  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 */ }

  19. Dependency Inversion

  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 */ }

  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 */ }

  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

  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; } }

  24. Passed Test?

  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; } }

  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; } }

  27. Why We Write Stubs Testing NuclearPowerPlant NOT NuclearReactor

  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

  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; } }

  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

  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 or

  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()); }

  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

  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

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

  36. Passed Test?

  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); }

Recommend


More recommend