Good Programming Flashcards
How to run tests and debug code. Exceptions and assertions
Defensive Programming
- Specifications for functions
- Modularize programs
- Check conditions on input/outputs (assertions)
Testing/validation
- Compare input/output to specs
- It’s not working!
- How can I break my program?
Debugging
- Study events leading up to the error
- Why is it not working?
- How can I fix my program?
Set yourself up for testing/debugging
- design code to ease this part
- break programs into modules that can be tested and debugged individually
- document assumptions
Ready to test?
Ensure code runs - remove syntax errors - remove static semantic errors - Python Interpreter can usually find these Set of Expected results - and input set - for each input a corresponding output
Classes of tests
- Unit testing
- Regression testing
- Integrations testing
Unit testing
- validate each piece of the program
- testing each function separately
Regression testing
- add test for bugs as you find them in a function
- catch reintroduced errors that were previously fixed
Integration testing
- does overall program work?
- tend to rush to do this
Testing methods
- Intuition
- Random testing
- Black Box Testing
- Glass Box Testing
Intuition
Pick natural boundaries to the problem
Random testing
If no natural partitions
- Probability that code is correct increases with more test
- Better options with Black Box and Glass Box
Black Box Testing
Explore paths through specifications
Glass Box Testing
Explore paths through code
More about Black Box testing
- Designed without looking at the code
- Avoid biases implementations
- Testing can be reused
Paths through specification - build test cases in different natural space partitions
Considerations for Black Box testing
- empty lists
- singleton lists
- large numbers
- small numbers
More about Glass Box testing
- Use code directly to guide design of test cases
- Called path-complete if every potential path through code is tested at least once
Drawback for Glass Box testing
- can go through loops arbitrary many times
- missing paths
Guidelines for Glass Box testing
Branches - exercise all parts of a conditional for loops - loop not entered - body of loop executed exactly once - body of loop executed more than once while loops - same as for loops
Runtime bugs
- Overt
- Covert
- Persistent
- Intermittent
Overt
- Has an obvious manifestation
- Code crashes or runs forever
Covert
- Has no obvious manifestation
- Code returns a value, which may be incorrect but hard to determine
Persistent
- Occurs every time code is run
Intermittent
- Only occurs some times
- even if run on same input
Overt and Persistent
- Obvious to detect
- Use defensive programming to try to ensure that if error is made, bug will fall into this category
Overt and Intermittent
- More frustrating
- Can be harder to debug
- If condition that prompt bug can be reproduced -> can be handled
Covert
- Highly dangerous
- users may not realize answers are incorrect until code has been run for a long period
Debugging Tools
- Built in the IDE or code editor
- Python Tutor
- print statement
- use your brain -> be systematic
Print statements
- Test hypothesis
- Bisection method
When to Print
- Enter function
- Parameter
- Function result
Print using Bisection method
- Put print halfway in code
- Decide where bug may be depending on value
Logic errors
- Think before writing new code
- Draw picture - take a break
- Explain the code to (someone else or a rubber duck)
Debugging steps
- Study program code
- Scientific method
Debugging using study method
- how did I get the unexpected result
- dont’ ask what is wrong
- is it part of a family
Debugging using scientific method
- study available data
- form hypothesis
- repeatable experiments
- pick simplest input to test with
Testing does
- Write a function
- Test the function, debug the function
- Write a function
- Test the function, debug the function
- ** Do integration testing **
Debugging does
- Backup code
- Change code
- Write down potential bug in a comment
- Test code
- Compare new and old versions
Debugging skills
Treat as a search problem Looking for explanation for incorrect behavior - Study available data - Form hypothesis - Design and run a repeatable experiment - Keep record of experiments performed
Debugging as search
- narrow down space of possible source of error
- design experiments that expose intermediate stages of computation
- use print statements
- use results to further narrow search
- binary search is a powerful tool
Pragmatic hints for debugging
- look for usual suspects
- ask why the code is doing what it is
- the bug is probably not where you think it is
- don’t believe the documentation
- take a break
Exceptions
When a procedure execution hits an unexpected conditions
IndexError
Trying to access beyond lists limits
TypeError
Trying to convert an inappropriate type
Mixing data types without coercion
NameError
Referencing a non-existing variable
ZeroDivisionError
Trying to divide with 0
ValueError
Operand type okay, but value is illegal
IOError
IO system reports malfunction (eg. file not found)
AttributeError
Local or global name not found
What to do with Exceptions
- Fail silently -> bad idea!
- Return an “error” value -> what value to choose, complicates code
- Stop execution, signal error condition -> raise an exception
Dealing with exceptions
- exceptions raised by any statement of try
- handled by the except and execution continues after the except statement
Handling specific exceptions
Have separate except clauses to deal with particular type of exception
Other exceptions
- else -> is executed when execution of try body completes with no exceptions
- finally -> always executed after: try, else and except clauses.
- finally is executed even if they raised another error or executed a break, continue or return
- finally is useful for clean-up code that should be run no matter what else happened
Exceptions as control flow
- Raise an exception when unable to produce a result consistent with functions specification
Assertions
- Use assert statement to raise an AssertaionError
- An example of good defensive programming
Assertions as defensive programming
- Don’t allow a programmer to control response from to unexpected conditions
- Ensure the execution halts
- Typically used to check inputs to functions
- Can be used to check outputs of a function
- Can make it easier to locate a source of a bug
Where to use assertions
- Goal to spot bugs
- Supplement to testing
- Raise exceptions if bad data is inputted
- Check types
- Check that in-variants on data structures are met
- Check constraints on return value
- Check for violations of constraints on procedure (e.g. no duplicates in a list)