3

I am setting up Coded UI Tests for WPF application and I want to use code approach instead of record-and-generate-code approach. I'd like to use page objects trough code and I need to declare control (buttons, tabs, etc.) variables in page objects that would be used by multiple functions.

I tried declaring the variable in a class and adding properties in constructor (pendingButton1) and creating function which returns the control and assigning to a variable in a class (pendingButton2) but neither worked.

It works when I declare the variable (or create the variable by function) within the function that I want to use the variable in (pendingButton3 and 4).

public partial class Press : Header
{
    WpfToggleButton pendingButton1 = new WpfToggleButton(_wpfWindow);
    WpfToggleButton pendingButton2 = Controls.Press.getPendingButton(_wpfWindow);

    public Press(WpfWindow wpfWindow):base(wpfWindow)
    {
        this.pendingButton1.SearchProperties[WpfControl.PropertyNames.AutomationId] = "Tab1Button";
    }

    public void clickPendingButton() {
        WpfToggleButton pendingButton3 = new WpfToggleButton(_wpfWindow);
        pendingButton3.SearchProperties[WpfControl.PropertyNames.AutomationId] = "Tab1Button";

        WpfToggleButton pendingButton4 = Controls.Press.getPendingButton(_wpfWindow);

        Mouse.Click(pendingButton1); //UITestControlNotFoundException
        Mouse.Click(pendingButton2); //UITestControlNotFoundException
        Mouse.Click(pendingButton3); //This works
        Mouse.Click(pendingButton4); //This works
    }
}

I'd like to make it work when I declare the pendingButton outside clickPendingButton() function since it is used in multiple other functions.

  • So far, when I used my duck on a table, I found out that it might do something with inheritance - I inherit variable _wpfWindow from Header class, hence, I should use initialize class variables in constructor with _wpfWindow inherited variable. Anyway, that seems to me like a little overhead to create variable in class and initialize it within constructor and then add Search properties. – Marty Janík Jan 11 '19 at 12:04
  • 1
    Coding yourself is fine, but let Coded UI's record and generate teach you. Use it in a sandbox project and copy the bits you need. – AdrianHHH Jan 11 '19 at 14:13
  • Do note that record and generate is not useful when it comes to controls that get dynamically added to the UI under test. E.g. grid rows that can contain different information every time you start your test. In such a case, you could use the `getChildren()` function on the parent (table) containing those rows. – PixelPlex Jan 14 '19 at 07:56

2 Answers2

1

The helper function Controls.getWpfButton() return just properties of the button, not "real" button. It has to be used in a constructor, then it can be used anywhere within the class. I wouldn't say its best practice but it works for me.

Press.cs

public partial class Press : SharedElements
    {
        private WpfButton pendingButton;

    public Press(WpfWindow wpfWindow):base(wpfWindow)
        {
            pendingTab = Controls.getWpfButton(_wpfWindow, "Tab1Button");
        }

    public void clickPendingButton() {
        Mouse.Click(pendingButton);
    }
}

Controls.cs

internal static WpfButton getWpfButton(WpfWindow wpfWindow, string AutomationId)
    {
        WpfButton button = new WpfButton(wpfWindow);
        button.SearchProperties[WpfControl.PropertyNames.AutomationId] = AutomationId;
        return button;
    }
0

What you want appears to be exactly the sort f code that the Coded UI record and generate tool generates. It creates many pieces of code that have a structure of the following style:

public WpfToggleButton PendingButton
{
    get
    {
        if ((this.mPendingButton == null))
        {
            this.mPendingButton = new WpfToggleButton( ... as needed ...);
            this.mPendingButton.SearchProperties[ ... as needed ...] = ... as needed ...;
        }

        return this.mPendingButton;
    }
}

private WpfToggleButton mPendingButton;

This code declares the button as the class property PendingButton with a private supporting field that has an initial and default value of null. The first time that property is needed the get code executes the required search and saves the found control in the private field. That value is then returned in each subsequent usage of the property. Note that assigning null to the supporting field can be done to cause a new search, as demonstrated in this Q&A.

AdrianHHH
  • 13,492
  • 16
  • 50
  • 87
  • Thanks for the answer. I was inspired by generated code but I went with another approach. I created a helper function getWpfButton(WpfWin, AutomationId) which returns just properties of the button. I believe, the button is then searched when it is used (for example by Mouse.Click()). To be clear - I declared pending button as class variable, I assigned it to the helper function in the constructor. Anyway, if I encounter any issues, I'll go with this approach. – Marty Janík Jan 14 '19 at 08:46
  • @MartyJaník I am not fully following your comment. It may be helpful to other people if you write a proper answer explaining how you solved the problem. – AdrianHHH Jan 14 '19 at 08:52