Presentation is loading. Please wait.

Presentation is loading. Please wait.

Approaches to Automatically Testing Visual Components Richard B. Winston CPCUG Programmers SIG Nov. 1, 2006.

Similar presentations


Presentation on theme: "Approaches to Automatically Testing Visual Components Richard B. Winston CPCUG Programmers SIG Nov. 1, 2006."— Presentation transcript:

1 Approaches to Automatically Testing Visual Components Richard B. Winston CPCUG Programmers SIG Nov. 1, 2006

2 Outline Review of DUnit Review of DUnit Difficulties Using Dunit with Visual Components Difficulties Using Dunit with Visual Components Simulating User Interaction in DUnit Simulating User Interaction in DUnit Comparing Appearances of Visual Components Comparing Appearances of Visual Components Limitations Limitations

3 Review of DUnit Testing Framework for Delphi Testing Framework for Delphi Open Source: http://dunit.sourceforge.net/ Open Source: http://dunit.sourceforge.net/ http://dunit.sourceforge.net/ Built-in support in BDS 2005 and 2006 Built-in support in BDS 2005 and 2006 Works with Delphi >= 5 Works with Delphi >= 5

4 DUnit Example: 1 type // Test methods for class TRbwParser // Test methods for class TRbwParser TestTRbwParser = class(TTestCase) TestTRbwParser = class(TTestCase) strict private strict private FRbwParser: TRbwParser; FRbwParser: TRbwParser; public public procedure SetUp; override; procedure SetUp; override; procedure TearDown; override; procedure TearDown; override; published published procedure TestClearExpressions; procedure TestClearExpressions; … end; end;

5 DUnit Example: 2 procedure TestTRbwParser.TestClearExpressions; var Expression: string; Expression: string;begin if FRbwParser.ExpressionCount = 0 then if FRbwParser.ExpressionCount = 0 then begin begin Expression := '1+2'; Expression := '1+2'; FRbwParser.Compile(Expression); FRbwParser.Compile(Expression); end; end; Check(FRbwParser.ExpressionCount > 0, 'Error in counting expressions'); Check(FRbwParser.ExpressionCount > 0, 'Error in counting expressions'); FRbwParser.ClearExpressions; FRbwParser.ClearExpressions; Check(FRbwParser.ExpressionCount = 0, 'Error in clearing expressions'); Check(FRbwParser.ExpressionCount = 0, 'Error in clearing expressions');end;

6 DUnit in Action

7 Manually Testing User Interaction Manual Manual –Ask the user to do something –Let the user decide if the results are correct Problems Problems –Slow –Unreliable –Discourages Testing

8 Automatically Test User Interaction Simulate Mouse and Keyboard Events Simulate Mouse and Keyboard Events Get Screen Captures of the Controls Get Screen Captures of the Controls Compare the New Screen Captures to Old Ones Compare the New Screen Captures to Old Ones Borland’s private “Zombie” program does something similar. See http://www.stevetrefethen.com/files/index.html Borland’s private “Zombie” program does something similar. See http://www.stevetrefethen.com/files/index.html

9 Windows API for Simulating the Mouse and Keyboard Mouse_Event( MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0); Mouse_Event( MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0); Keybd_Event(key, MapvirtualKey(key, 0), flag, 0) Keybd_Event(key, MapvirtualKey(key, 0), flag, 0)

10 Simulate Mouse and Keyboard // move the mouse to cell [0,1] // move the mouse to cell [0,1] APoint := CellCenter(frmGridTest.TestGrid, 0,1); APoint := CellCenter(frmGridTest.TestGrid, 0,1); MoveMouseToPosition(APoint); MoveMouseToPosition(APoint); // double click on the cell to start editing text. // double click on the cell to start editing text. DoubleClick; DoubleClick; // enter some text. // enter some text. SimultateText('abcd efgh ijkl'); SimultateText('abcd efgh ijkl');

11 CellCenter function CellCenter(Const Grid: TCustomDrawGrid; Const ACol, ARow: integer): TPoint; var ARect: TRect; ARect: TRect;begin ARect := Grid.CellRect(ACol,ARow); ARect := Grid.CellRect(ACol,ARow); result.X := (ARect.Left + ARect.Right) div 2; result.X := (ARect.Left + ARect.Right) div 2; result.Y := (ARect.Top + ARect.Bottom) div 2; result.Y := (ARect.Top + ARect.Bottom) div 2; result := Grid.ClientToScreen(result); result := Grid.ClientToScreen(result);end;

12 MoveMouseToPosition Procedure MoveMouseToPosition(Position: TPoint); Begin Mouse.CursorPos := Position; Mouse.CursorPos := Position; Application.ProcessMessages; Application.ProcessMessages;end;

13 MouseClick procedure MouseGoesDown; begin Mouse_Event( MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0); Mouse_Event( MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);end; procedure MouseGoesUp; begin Mouse_Event( MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); Mouse_Event( MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);end; procedure MouseClick; begin MouseGoesDown; MouseGoesDown; MouseGoesUp; MouseGoesUp;end; procedure DoubleClick; begin MouseClick; MouseClick; end;

14 DragMouse procedure DragMouse(Start, Stop: TPoint); Const TenthSecond = 100; // 100 milliseconds - 1/10 second. This value works for me. TenthSecond = 100; // 100 milliseconds - 1/10 second. This value works for me.begin MoveMouseToPosition(Start); MoveMouseToPosition(Start); MouseGoesDown; MouseGoesDown; try try MoveMouseToPosition(Stop); MoveMouseToPosition(Stop); // To generate a mouse move event, enough time must elapse. // To generate a mouse move event, enough time must elapse. // Empirically 1/20'th of a second is enough. // Empirically 1/20'th of a second is enough. Sleep(TenthSecond); Sleep(TenthSecond); Application.ProcessMessages; Application.ProcessMessages; finally finally MouseGoesUp; MouseGoesUp; Application.ProcessMessages; Application.ProcessMessages; end; end;end;

15 SimultateText Procedure SimultateText(const AString: string); var CharIndex: integer; CharIndex: integer; AChar: Char; AChar: Char; KeyCode: TKeyCode; KeyCode: TKeyCode; Shift: TShiftState; Shift: TShiftState;begin for CharIndex := 1 to Length(AString) do for CharIndex := 1 to Length(AString) do begin begin AChar := AString[CharIndex]; AChar := AString[CharIndex]; KeyCode.LongKeyCode := VkKeyScan(AChar); KeyCode.LongKeyCode := VkKeyScan(AChar); Shift := []; Shift := []; if (KeyCode.Shift and 1) <> 0 then if (KeyCode.Shift and 1) <> 0 then begin begin Include(Shift, ssShift); Include(Shift, ssShift); end; end; if (KeyCode.Shift and 2) <> 0 then if (KeyCode.Shift and 2) <> 0 then begin begin Include(Shift, ssCtrl); Include(Shift, ssCtrl); end; end; if (KeyCode.Shift and 4) <> 0 then if (KeyCode.Shift and 4) <> 0 then begin begin Include(Shift, ssAlt); Include(Shift, ssAlt); end; end; PostKeyEx32(KeyCode.KeyCode, Shift, False); PostKeyEx32(KeyCode.KeyCode, Shift, False); end; end;end;

16 PostKeyEx32 // from http://delphi.about.com/od/adptips2004 /a/bltip0604_4.htm procedure PostKeyEx32(key: Word; const shift: TShiftState; specialkey: Boolean) ; type TShiftKeyInfo = record TShiftKeyInfo = record shift: Byte ; shift: Byte ; vkey: Byte ; vkey: Byte ; end; end; ByteSet = set of 0..7 ; ByteSet = set of 0..7 ;const shiftkeys: array [1..3] of TShiftKeyInfo = shiftkeys: array [1..3] of TShiftKeyInfo = ((shift: Ord(ssCtrl) ; vkey: VK_CONTROL), ((shift: Ord(ssCtrl) ; vkey: VK_CONTROL), (shift: Ord(ssShift) ; vkey: VK_SHIFT), (shift: Ord(ssShift) ; vkey: VK_SHIFT), (shift: Ord(ssAlt) ; vkey: VK_MENU)) ; (shift: Ord(ssAlt) ; vkey: VK_MENU)) ;var flag: DWORD; flag: DWORD; bShift: ByteSet absolute shift; bShift: ByteSet absolute shift; j: Integer; j: Integer;begin for j := 1 to 3 do for j := 1 to 3 do begin begin if shiftkeys[j].shift in bShift then if shiftkeys[j].shift in bShift then keybd_event(shiftkeys[j].vkey, keybd_event(shiftkeys[j].vkey, MapVirtualKey(shiftkeys[j].vkey, 0), 0, 0) ; MapVirtualKey(shiftkeys[j].vkey, 0), 0, 0) ; end; end; if specialkey then if specialkey then flag := KEYEVENTF_EXTENDEDKEY flag := KEYEVENTF_EXTENDEDKEY else else flag := 0; flag := 0; keybd_event(key, MapvirtualKey(key, 0), flag, 0) ; keybd_event(key, MapvirtualKey(key, 0), flag, 0) ; flag := flag or KEYEVENTF_KEYUP; flag := flag or KEYEVENTF_KEYUP; keybd_event(key, MapvirtualKey(key, 0), flag, 0) ; keybd_event(key, MapvirtualKey(key, 0), flag, 0) ; for j := 3 downto 1 do for j := 3 downto 1 do begin begin if shiftkeys[j].shift in bShift then if shiftkeys[j].shift in bShift then keybd_event(shiftkeys[j].vkey, keybd_event(shiftkeys[j].vkey, MapVirtualKey(shiftkeys[j].vkey, 0), MapVirtualKey(shiftkeys[j].vkey, 0), KEYEVENTF_KEYUP, 0) ; KEYEVENTF_KEYUP, 0) ; end; end;end;

17 Comparing Appearances of Visual Components PaintControlToBitMap(frmGridTest.TestGrid, PaintControlToBitMap(frmGridTest.TestGrid, TestBitMap, True); TestBitMap, True); frmGridTest.Hide; frmGridTest.Hide; BitmapKey := BitmapKey := RbwDataGridAutoWordWrapAdjustRowHeight; RbwDataGridAutoWordWrapAdjustRowHeight; UserPrompt := UserPrompt := 'Has the text in the string and boolean cells word wrapped ' 'Has the text in the string and boolean cells word wrapped ' + 'and have the rows expanded to accommodate the text?'; + 'and have the rows expanded to accommodate the text?'; Result := CompareAndUpdateBitmap(BitmapKey, UserPrompt, Result := CompareAndUpdateBitmap(BitmapKey, UserPrompt, TestBitMap); TestBitMap);

18 PaintControlToBitMap Procedure PaintControlToBitMap(Control: TControl; BitMap: TBitMap; IncludeCursor: boolean); BitMap: TBitMap; IncludeCursor: boolean);begin // Set the size of the bitmap that will hold the image of Control BitMap.Width := Control.Width; BitMap.Width := Control.Width; BitMap.Height := Control.Height; BitMap.Height := Control.Height; if Control is TWinControl then if Control is TWinControl then begin begin TWinControl(Control).PaintTo(BitMap.Canvas, 0, 0); TWinControl(Control).PaintTo(BitMap.Canvas, 0, 0); end end else if Control is TGraphicControl then else if Control is TGraphicControl then begin begin // Get a screen capture of the form containing the TGraphicControl // Get a screen capture of the form containing the TGraphicControl // and copy only the portion of that screen capture that contains // and copy only the portion of that screen capture that contains // the TGraphicControl into BitMap. // the TGraphicControl into BitMap.

19 CompareAndUpdateBitmap function CompareAndUpdateBitmap(const BitmapKey, UserPrompt: string; TestBitMap: TBitmap): Boolean; TestBitMap: TBitmap): Boolean;var PredefinedBitMap: TBitmap; PredefinedBitMap: TBitmap;begin PredefinedBitMap := FBitmapStorage.BitMapCollection.Find( BitmapKey); PredefinedBitMap := FBitmapStorage.BitMapCollection.Find( BitmapKey); result := CompareBitMaps(TestBitMap, PredefinedBitMap); result := CompareBitMaps(TestBitMap, PredefinedBitMap); if not result and (TestMethod = tmScripted) then if not result and (TestMethod = tmScripted) then begin begin result := UserCompareBitMap(PredefinedBitMap, TestBitMap, UserPrompt); result := UserCompareBitMap(PredefinedBitMap, TestBitMap, UserPrompt); if result then if result then begin begin if PredefinedBitMap = nil then if PredefinedBitMap = nil then begin begin FBitmapStorage. FBitmapStorage. BitMapCollection.Store(BitmapKey, BitMapCollection.Store(BitmapKey, TestBitMap); TestBitMap); end end else else begin begin PredefinedBitMap.Assign( PredefinedBitMap.Assign( TestBitMap); TestBitMap); end; end; end;

20 Limitations Someone has to check the appearance of a control manually at least once. Someone has to check the appearance of a control manually at least once. There can be no manual use of the mouse or keyboard during a test. There can be no manual use of the mouse or keyboard during a test. Changes to the computer outside of the control of the test, such as changing from small to large fonts, will affect the bitmap comparisons. Changes to the computer outside of the control of the test, such as changing from small to large fonts, will affect the bitmap comparisons.

21 Summary DUnit can be used to test visual controls. DUnit can be used to test visual controls. Manual user interaction during tests can be reduced but not entirely eliminated. Manual user interaction during tests can be reduced but not entirely eliminated. The tests are fragile – They are easily affected by changes to the computer outside the test. The tests are fragile – They are easily affected by changes to the computer outside the test.


Download ppt "Approaches to Automatically Testing Visual Components Richard B. Winston CPCUG Programmers SIG Nov. 1, 2006."

Similar presentations


Ads by Google