Loading ...
Sorry, an error occurred while loading the content.

Re: [XP] How do you handle UI tests?

Expand Messages
  • Kevin Lawrence
    From: ... There are lots of ways. A lot of people take the approach of making the UI layer as thin as possible and pushing all the
    Message 1 of 5 , Aug 22, 2002
    • 0 Attachment
      From: <hammett@...>
      >
      > But now I'm facing the winforms and asp.net tests. How do you handle it?
      >
      > I had asked this question before to Kevin Lawrence. His reply was helpful,
      > but I still need more pratical information.
      >

      There are lots of ways.

      A lot of people take the approach of making the UI layer as thin as possible
      and pushing all the complex logic into a *model* class. Then they test the
      model.

      Michael Feathers describes this approach here
      http://www.objectmentor.com/resources/articles/TheHumbleDialogBox.pdf

      Ron Jeffries has an example here
      (no doubt he'll point you to a more general description real soon now)
      http://www.xprogramming.com/xpmag/acsExtractClass.htm


      With Windows.Forms I have found this unnecessary. I still try to separate
      model from view ... but my model is pure business logic and knows nothing
      about the UI. I leave all the UI logic (enable/disable, focus management,
      validating input) in the UI classes - and test the UI classes directly.

      I posted a simple example before to give the flavor - maybe you need a more
      complex one ?

      There are several techniques I use.

      Sometimes a UI gesture results in a change in the UI. For example, check
      this checkbox and that button is disabled....that's the case in the example
      I posted before.

      Sometimes a UI gesture results in a change in the model... click this button
      to add a story.... drag a story to the end of iteration 2. In my code, UI
      actions that change the model work via a Command object .... so I separate
      the test into

      1) make sure the UI executes the right command
      2) make sure the command works right

      Here's an example of each :

      Invoking a command :

      public class TestIterateToolbar : TestCase
      {
      IterateToolBar toolbar;
      MockCommandProcessor processor;

      protected override void SetUp()
      {
      MockGlobalContext context = new MockGlobalContext();
      toolbar = new IterateToolBar(context);
      processor = context.commandProcessor;
      }

      public void TestNewStory()
      {
      toolbar.PerformClick(11);
      Assert(processor.command is NewStoryCommand);
      }
      }

      in this case, processor, is a mock object that does not actually execute the
      command.


      Testing a command :

      public class TestNewStoryCommand : TestCommand
      {
      new TestableNewStoryCommand command;

      protected override Command CreateCommand()
      {
      command = new TestableNewStoryCommand();
      return command;
      }

      public override void TestExecute()
      {
      command.Execute();
      AssertEquals("changed text", project.storyAdded.Text);
      AssertEquals(ScheduleOptions.NextIteration, project.scheduleOption);
      }

      }

      In this case, the base class has a bunch of boiler plate tests for things
      that I often forget for a particular command - is there a short cut ? is
      there a tool tip ? Does it enable/disable correctly if the command depends
      on selection.

      This example also demonstrates another technique I use for testing modal
      dialogs. I put the call to ShowDialog() into a virtual method ....

      protected virtual void ShowDialog(StoryProperties dialog)
      {
      dialog.ShowDialog(Context.TopLevelForm);
      }

      and override it in a *Testable* subclass of the command that does not show
      the dialog but tests that it has been initialized correctly, like this.

      class TestableNewStoryCommand : NewStoryCommand
      {
      public ScheduleOptions expectedDefaultOption =
      ScheduleOptions.NextIteration;
      public ScheduleOptions optionToSet = ScheduleOptions.EndOfSchedule;
      public int expectedDefaultIteration = 0;
      public DialogResult result = DialogResult.OK;

      protected override void ShowDialog(StoryProperties dialog)
      {
      AssertEquals(expectedDefaultOption, dialog.ScheduleOption);
      AssertEquals(expectedDefaultIteration, dialog.IterationNumber);

      dialog.ScheduleOption = optionToSet;
      dialog.CommitChanges();
      dialog.DialogResult = result;

      AssertEquals("New Story", dialog.Text);
      }
      }


      In this case, I am also using the overriden ShowDialog() as a hook to
      simulate the user entering new information in the dialog before hitting OK.

      One more example - drag and drop -

      public class TestStoryPanelDragDropHandler : TestCase
      {
      public void TestStoryDragged()
      {
      AddStory();
      DragStory(panel.StoryControls[0]);
      AssertEquals(0, iteration.Stories.Count);
      }

      public void TestDrop()
      {
      Story story = new Story();
      Drop(story, 0);
      AssertEquals(1, iteration.Stories.Count);
      AssertEquals(story, iteration.Stories[0]);
      }

      void DragStory(StoryControlBase storyControl)
      {
      ItemDragEventArgs args = new ItemDragEventArgs(MouseButtons.Left,
      storyControl.Story);
      handler.Story_DragStarted(storyControl, args);
      AssertEquals(storyControl.Story, handler.draggedStory);
      }

      void Drop(object data, int x)
      {
      handler.StoryPanel_DragDrop(panel, CreateDragEvent(data, x, 0));
      }

      public void TestDropZoneMarkerPositionNoStories()
      {
      DragOver(new Story(), 0, 0);
      AssertEquals(new Point(0, 0), handler.DropZoneMarker.Position);
      }

      DragDropEffects DragOver(object data, int x, int y)
      {
      DragEventArgs args = CreateDragEvent(data, x, y);
      handler.StoryPanel_DragOver(panel, args);
      return args.Effect;
      }
      }

      (I snipped this a lot to try to capture the essence - there are a lot of
      edge cases).

      A lot of UI gestures in .NET can be activated programmatically - like
      button.PerformClick()
      For others, you can fake it at a lower level by creating MouseEvents or, as
      in this case, drag events and drop events.

      Other techniques I use ... if I am testing a UI widget - I'll usually have a
      CaptureWidgets() method in my test fixure like this :

      void CaptureWidgets()
      {
      storyText = (TextBox) dialog.Controls[0];
      estimates = (GroupBox) dialog.Controls[1];
      scheduleOption = (ComboBox) dialog.Controls[2].Controls[0];
      iterationNumber = (NumericUpDown) dialog.Controls[2].Controls[1];
      completed = (CheckBox) dialog.Controls[2].Controls[2];

      ok = (Button) dialog.Controls[3].Controls[0];
      cancel = (Button) dialog.Controls[3].Controls[1];
      }

      So the individual tests don't have to deal with finding the widget they
      need...

      public void TestButtons()
      {
      AssertEquals("&OK", ok.Text);
      AssertEquals("&Cancel", cancel.Text);
      }

      I don't check exact positioning of controls because it's not worth the
      trouble but I do use a layout manager for dialogs and the layout manager has
      tests. I don't use the visual dialog builder wizard thing at all. I find it
      easier to build dialogs programmatically - and it's certainly easier to test
      that way.

      I have written exactly one ASP.NET page with 1 WebControl. I didn't do it
      test first - but I suspect you could use a lot of the same techniques
      because WebControls are quite modular. I have worked with the equivalent
      J2EE technology - taglibs - extensively, and I tested them at the Http level
      using HttpUnit. My guess is that WebControls would be easy to unit test
      because they are more self-contained..

      Not sure if I covered the kind of stuff you are looking for, Hammet. Let me
      know if there's anything in particular you are having trouble with. I
      recommend looking at the other articles too, because there are many ways to
      approach this problem.

      For anyone else - this was all stream of consciousness stuff. I can write it
      up a little more coherently if it's useful. Let me know.


      Kevin

      Diamond Sky Software
      www.diamond-sky.com
    • hammett@vesta.com.br
      It cleared a lot. Thanks again Kevin! -- hammett ... From: Kevin Lawrence [mailto:kevin@diamond-sky.com] Sent: quinta-feira, 22 de agosto de 2002 18:10 To:
      Message 2 of 5 , Aug 22, 2002
      • 0 Attachment
        It cleared a lot.
        Thanks again Kevin!

        --
        hammett




        -----Original Message-----
        From: Kevin Lawrence [mailto:kevin@...]
        Sent: quinta-feira, 22 de agosto de 2002 18:10
        To: extremeprogramming@yahoogroups.com
        Subject: Re: [XP] How do you handle UI tests?


        From: <hammett@...>
        >
        > But now I'm facing the winforms and asp.net tests. How do you handle it?
        >
        > I had asked this question before to Kevin Lawrence. His reply was helpful,
        > but I still need more pratical information.
        >

        There are lots of ways.

        A lot of people take the approach of making the UI layer as thin as possible
        and pushing all the complex logic into a *model* class. Then they test the
        model.

        Michael Feathers describes this approach here
        http://www.objectmentor.com/resources/articles/TheHumbleDialogBox.pdf

        Ron Jeffries has an example here
        (no doubt he'll point you to a more general description real soon now)
        http://www.xprogramming.com/xpmag/acsExtractClass.htm



        [Non-text portions of this message have been removed]
      • Chris Morris
        ... Morris ... Yeup. I refactored stuff down from my IE testing to also allow for COM interop to the WinForms so I could test from Ruby. If you register your
        Message 3 of 5 , Aug 23, 2002
        • 0 Attachment
          > We have used this on one small project at my work, and I think Chris
          Morris
          > has done a similar thing. Come to think of it, I think he may have done a
          > similar thing with WinForms as well.

          Yeup. I refactored stuff down from my IE testing to also allow for COM
          interop to the WinForms so I could test from Ruby. If you register your
          WinForm as a COM object, then you talk to it quite easily:

          # Ruby-from-memory code, actual code may differ :)
          require 'win32ole'

          form = WIN32OLE.new("MyDotNetNamespace.MyCompany.MyProject.MyForm")
          form.User = 'me'
          form.Password = 'password'
          form.Submit.click

          The IE ruby stuff can be dug out here: http://clabs.org/iec -- my forms
          stuff isn't in there yet, but could be if you beg :)

          Chris
          http://clabs.org
        Your message has been successfully submitted and would be delivered to recipients shortly.