Testing and Debugging Your Application
Lesson 1
Error Types
Syntax Errors
- Compiler cannot
process provided source code – e.g. keyword wrongly typed
- Build project
-> errors detected, underlined and added to task list
- Double click
error in task list – associated code highlighted
- Obtain help by
pressing F1
- Minimal syntax
checking provided whilst typing in code
Run Time
- App performs
illegal operation
- Divide by
zero
- Security
exceptions
- Exception
describing error is thrown – write code to handle it
Logical
- Correct
execution but unexpected results
- e.g. calculate
pay but multiply hours per week by 400, not 40
Break Mode
- Halt program
execution and step through line at a time
- Enter on
following conditions:
- Choose to
Step Into, Over or Out
- Execution
reaches breakpoint
- Execution
reaches stop statement
- Unhandled
exception thrown
Breakpoints
- Function –
stop at specified location in function
- Click grey
bar to left of code line to stop at
- Right click
on desired line and insert breakpoint
- Insert via
Debug menu
- File –
specified location in file
- Address – when
specified memory address accessed
- Breakpoint
window
- manage all
breakpoints
- display
information
- create,
delete or disable
Debugging Windows
- Output –
command line output, debug and trace statements
- Locals (all
vars), Autos (current and previous line vars), Watch (chosen vars) –
monitor program variables
- Command –
execute procedures, evaluate expressions, change vars
- Cannot accept
data declarations
- In Break mode
- if enter
statement / method call IDE switches to runtime, executes statement,
return to break mode
- print
variable values – use ? operator
Lesson 2
Debug and Trace
- Debug class –
log messages during execution (of debug code)
- Trace class –
log messages during execution (both debug and release code)
- Both classes
contain static methods to test conditions and log data
- Class output
sent to listeners (and Output window)
Methods
- Write – write
to listeners unconditionally
- WriteLine –
write to listeners unconditionally followed by CR
- WriteIf –
write to listeners if Boolean expression true
- WriteLineIf -
write to listeners unconditionally followed by CR if Boolean expression
true
- Assert –
displays message box and writes to listeners if Boolean expression
false
- Fail –
displays message box and writes to listeners
Listeners Collection
- Listeners
collection organises and
exposes classes that can receive trace output
- Listeners
collection initialised with DefaultTraceListener class – receives
messageseven if no other listeners attached
- DefaultTraceListener
directs trace to IDE output window
- 2 base trace
classes
- EventLogTraceListener
– directs output to event log
- TextWritertraceListener
– directs output to stream or TextWriter
// Open (or create)
file
System.IO.FileStream myLog = new
System.IO.FileStream(“C:\\myfile.txt”,
System.IO.FileMode.OpenOrCreate);
// Create TraceListener logging to
specified file
TextWriterTraceListener myListener =
new TextWriteTraceListener(myLog);
// Add to listeners
collection
Trace.Listeners.Add(myListener);
- To write to
file must flush, either by explicitly calling trace method or setting
AutoFlush property to true
- Using
EventLogTraceListener similar
- Create new
EventLog
- Create
EventLogTraceListener class
- Add to
listeners collection
// Create event log entitled
“DebugLog”
EventLog myLog = new EventLog(“Debug
Log”);
// Set EventLog source property (avoid
error)
myLog.source = “Trace
Output”;
// Create
EventLogTraceListener
EventLogTraceListener = new
EventLogTraceListener(myLog);
Trace Switches
- 2 types –
BooleanSwitch class (on or off) and TraceSwitch class (five values)
- Both classes
require DisplayName (its name in configuration file) and
Description
BooleanSwitch myBoolSwitch =
newBooleanSwitch(“Switch1”, “Control Data Tracing”);
TraceSwitch myTraceSwitch = new
TraceSwitch(“Switch2”, “Control Form Tracing”);
- TraceSwitch = 5
settings, exposed by TraceSwitch.Level property
- TraceLevel.Off
- TraceLevel.Error
- TraceLevel.Warning
- TraceLevel.Info
- TraceLevel.Verbose
- TraceSwitch
class exposed four Boolean properties corresponding to trace levels of
same name, e.g. if TraceSwitch.Level = TraceLevel.Info then
TraceSwitch.TraceInfo is true (as are TraceSwitch.TraceError and
TraceSwitch.TraceWarning) while TraceSwitch.TraceVerbose is false
- No auto hook-up
between trace switches and statements, use TraceSwitch to test if output
required:
Trace.WriteIf(myBoolSwitch.Enabled ==
true, “error”);
Trace.WriteIf(myTraceSwitch.TraceInfo
== true, “type mismatch”);
Configure Trace Switches
- Configured
within XML application .config file
- Config file
located in executables folder
- Config file
called appname.exe.config
- May need to
create config file (not all apps have them)
- When app
creates trace switch it checks .config file for info on switch
(identified by DisplayName)
- To create
file
o
Add new item from project menu
o
Choose text file and name appropriately
o
Type following
<?xml version=”1.0”
encoding=”Windows-1252”?>
<configuration>
<system.diagnostics>
<switches>
<add name=”myBoolSwitch” value=”0”/>
<add name=”myTraceSwitch” value=”3”/>
</switches>
</system.diagnostics>
</configuration>
Lesson 3
Unit Test Plan
- Run-time and
logical error snot detected without thorough testing
- Testing and
debugging separate but related activities
- Debug =
finding and correcting code errors
- Testing =
process by which errors found
- Testing
usually broken down by method, exercised using variety of params –
termed unit testing
- Cannot test
all permutations – use representative examples (test cases)
Test Case Design
- Minimum =
exercise all lines of code
- Design cases
to work through all decision branches
- App should
behave as expected when normal params provided
- App should
degrade gracefully when parms outside bounds provided
- Boundary
conditions – minimum and maximum and off by one
- Bad data –
values well outside range (0, negative, etc.)
- Data
combinations – test all method parms at boundary, bad values,
etc
- Determine
expected test case results prior to actual test
- Compare
obtained results with those expected
Lesson 4
Exceptions
- Structured
Exception Handling means of recovering gracefully from errors
- Exception =
instance of specialist class deriving from System.Exception
- Message
property = human readable description of error
- StackTrace
property = stack trace to pinpoint error location
- Run time
error occurs
- Exception
generated and passed up call stack to caller
- If
exception handler encountered it is handled, otherwise passed up stack
to next method… and so on
- If no
handler found default used – message box displayed and app
stopped
Exception Handler
- Implemented
on method basis – i.e. individually tailored to it and exceptions likely
to be thrown
- Wrap code
associated with handler in try block
- Add 1+
catch blocks to handle exceptions
- Add code
that must always be executed in finally block
public void Parse(string a
string)
{
try
{
double aDouble;
aDouble = Double.Parse(aString);
}
catch (System.ArgumentNullException e)
{
//
Code to handle null argument – e
//
contains ref to exception (access to
// its info)
}
catch
{
//
Catch any other exception
}
finally
{
//
Code that must be executed
}
}
- After code in
(only one) catch block executed, the finally block is run
- Write catch
blocks in order from most to least specific
- Can omit
catch block (exception handled further up call stack) but still include
finally block to execute any code in method that must run before
return
Throwing Exceptions
- 2
situations
- Only
partially handled exception and want to bubble it up call stack
- Unacceptable
condition occurred that cannot be handled locally and must be
communicated to parent – throw standard or custom exception
- Rethrow
exception by using throw
try
{
}
catch (System.NullReferenceException
e)
{
// Assuming
exception can not be handled
// rethrow
throw
e;
}
- Can pass
additional information on when rethrowing, e.g. to provide informative
message around original exception
throw new
NullReferenceException(“Widget A is not set”, e);
- Custom
exception – only for exceptional circumstances – not for use as
communication between client and components (use events). Only use when
conditions mean execution can not proceed without intervention
- Custom
exceptions derive from System.ApplicationException, e.g.
public class
WidgetException:System.ApplicationException
{
// Var to hold
widget
Widget
mWidget;
public Widget
ErrorWidget
{
get
{
return mWidget;
}
}
// Constructor
takes widget and string
//describing error
conditions
public
WidgetException(Widget W, string S) : base(S)
{
mWidget = W;
}
}
Widget Alpha;
throw new WidgetException(Alpha,”Alpha
is corrupt”);
|