2

I want to run integration UI tests on my WPF application, and I'm not sure how to detect when the current test has finished so that I can proceed to the next one.

Simplifying, suppose I have a button on my window. When the button is clicked I disable it, I modify the model, and I re-enable the button. Once it detects that the model has changed, WPF changes the view on the screen.

Now I want to run a test that simulates clicking the button again and again. To click the button I’ll use automation, as described in this SO question. But how do I know when the work is finished and the display updated, so as to "click" the button again? Do I hook the botton’s IsEnabledChanged, or is there some global indication that the current cycle of processing has finished?

Edit: What was missing in my description is that I want the user to see the interim results on the screen. For example, if the test has 10 phases I want the user to see something like a Step Counter label with values 1 .. 10 appearing on the screen, and not just the number changing immediately from 1 to 10. See my answer below.

Community
  • 1
  • 1
Avi
  • 15,696
  • 9
  • 39
  • 54

2 Answers2

1

how do I know when the work is finished and the display updated, so as to "click" the button again?

According to your description, you said When the button is clicked I disable it, I modify the model, and I re-enable the button.

Therefore, I can only assume that when the model has changed, the Button will be re-enabled. So you could either attach a handler to the model's NotifyPropertyChanged event, or as you suggested, add a handler for the IsEnabledChanged event.

Sheridan
  • 68,826
  • 24
  • 143
  • 183
  • The updates to the screen, if they occur, occur so fast I can't be sure that the screen is updated between intermediate testing steps. I'm up-voting your answer, but I'm not sure it's the correct method. See my edit. – Avi Mar 16 '15 at 06:57
0

Here is how I managed to get it working. This might be trivial - I'm a novice with GUI. I'm just posting it here in the hope it'll help other novices like me :)

Anyhow, what I used is a two button solutions: Test and Step. Test starts the testing sequence, Step runs each step of the tests. The Step buttons interact with an Integration Tester By Steps helper.

The helper receives an Init with the Number Of Commands as parameter, (currently the helper generates random commands by itself, so it just needs to know how many commands to generate). The helpe provides a Step method to execute the next command, and a Needs More Steps property to indicate whether testing should continue.

The helper derives form INotifyPropertyChanged and has a Counter dependency property that is displayed on the main window.

The states of the Test and Step buttons are controlled by three helper methods: SetButtonsFor_OutsideTesting, SetButtonsFor_InsideTestingOutsideAnyStep and SetButtonsFor_InsideTestingInsideAStep.

First, I verified that everything is working manually, and then I added a timer and automated the process using the Stack Overflow suggestions on how to programmatically click a button in WPF and how to make a WPF Timer Like C# Timer.

Now, here's the Main Window's code:

     private void Test_Click(object sender, RoutedEventArgs e)
    {
        SetButtonsFor_InsideTestingOutsideAnyStep();
        RunTheTestBySteps();
    }


    public readonly IntegrationTesterBySteps _integrationTesterBySteps =
                                            new IntegrationTesterBySteps();

    void RunTheTestBySteps()
    {
        SetButtonsFor_InsideTestingOutsideAnyStep();
        IntegrationTesterBySteps.Init(10);
        StartTheTimer();
    }

    private void StartTheTimer()
    {
        DispatcherTimer = new DispatcherTimer();
        DispatcherTimer.Tick += DispatcherTimer_Tick;
        DispatcherTimer.Interval = new TimeSpan(0, 0, 1);
        DispatcherTimer.Start();
    }

    private void StopTheTimer()
    {
        DispatcherTimer.Stop();
        DispatcherTimer.Tick -= DispatcherTimer_Tick;
    }

    private DispatcherTimer DispatcherTimer { get; set; }

    private void DispatcherTimer_Tick(object sender, EventArgs e)
    {
        if (!BtnStep.IsEnabled) return;
        ClickTheStepButton();
        CommandManager.InvalidateRequerySuggested();
    }

    private void BtnStep_Click(object sender, RoutedEventArgs e)
    {
        SetButtonsFor_InsideTestingInsideAStep();

        IntegrationTesterBySteps.Step();

        if (this.IntegrationTesterBySteps.NeedsMoreSteps)
            SetButtonsFor_InsideTestingOutsideAnyStep();
        else
        {
            SetButtonsFor_OutsideTesting();
            StopTheTimer();
        }
    }

    private void ClickTheStepButton()
    {
        var peer = new ButtonAutomationPeer(BtnStep);
        var invokeProv = peer.GetPattern(PatternInterface.Invoke)
                                              as IInvokeProvider;
        if (invokeProv != null)
            invokeProv.Invoke();
    }

    void SetButtonsFor_InsideTestingInsideAStep()
    {
        BtnTest.IsEnabled = false;
        BtnStep.IsEnabled = false;
    }

    void SetButtonsFor_InsideTestingOutsideAnyStep()
    {
        BtnTest.IsEnabled = false;
        BtnStep.IsEnabled = true;
    }

    void SetButtonsFor_OutsideTesting()
    {
        BtnTest.IsEnabled = true;
        BtnStep.IsEnabled = false;
    }
Community
  • 1
  • 1
Avi
  • 15,696
  • 9
  • 39
  • 54