FriendlyでE2Eテストを書いてみて、子メニューが取れずに苦戦


Windowsデスクトップアプリ(WPF)のE2Eテストを書きたくて、Friendlyに出会った。これが個人的に最も直感的だった。

詰まった点としては、Friendlyというより、WPFのLogicalTreeとVisualTreeの挙動というところにあると思われるので、同じこと悩んでる人がいたらいいなと思って、書きます。

メニューを動的に組んでいるケースでハマった

ログイン者に応じて、メニューを動的に切り替える仕組みを採用していて、そのXAMLがこんな感じです。

MainWindow.xaml

    <Grid Background="#FFF2F0DF">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Menu ItemsSource="{Binding GlobalMenu}">
            <Menu.ItemContainerStyle>
                <Style TargetType="{x:Type MenuItem}">
                    <Setter Property="Command" Value="{Binding DataContext.NavigateCommand, 
                        RelativeSource={RelativeSource FindAncestor,
                            AncestorType={x:Type view:MainWindow}}}"/>
                    <Setter Property="CommandParameter" Value="{Binding Value}"/>
                </Style>
            </Menu.ItemContainerStyle>
            <Menu.ItemTemplate>
                <HierarchicalDataTemplate DataType="{x:Type local:MainMenuModel}" ItemsSource="{Binding Path=Children}">
                    <TextBlock Text="{Binding Text}"/>
                </HierarchicalDataTemplate>
            </Menu.ItemTemplate>
        </Menu>
        <ContentControl prism:RegionManager.RegionName="ContentPage" Grid.Row="1" />
    </Grid>

システムを起動した直後は、GlobalMenunullなので、LogicalTreeには何も無い。Bindingによって、GlobalMenuに値がセットされると、階層構造に従ったメニューが構成されます。HierarchicalDataTemplateがそれで、値をバインドすればこういうメニューが作られます。

├── メニューA
   ├── メニューA1
   ├── メニューA2
   ├── メニューA3
├── メニューB
   ├── メニューB1
   ├── メニューB2
   ├── メニューB3
├── メニューC
   ├── メニューC1
   ├── メニューC2
   ├── メニューC3

この「メニューA1」等をクリックしたくて、試行錯誤したコードがこれ。

    [TestClass]
    public class MenuTest
    {
        private WindowsAppFriend _app;
        private WindowControl w;

        [TestInitialize]
        public void TestInitialize()
        {
            var path = System.IO.Path.GetFullPath("hoge.exe");
            _app = new WindowsAppFriend(Process.Start(path));
            w = _app.FromZTop();
        }
        [TestMethod]
        public void Testメニュー開け()
        {
            AppVar main = _app.Type<Application>().Current.MainWindow;
            var globalmenu = main.VisualTree().ByType<MenuItem>(); //Countすると3
            new WPFMenuItem(globalmenu[0]).EmulateClick(); //メニューAがクリックされる
            var uuum = globalmenu[0].visualTree().ByType<MenuItem>().Count; //これが0になるのが辛い
        }
    }
}

親のメニュー(メニューA,メニューB,メニューC)は、VisualTreeを辿ったら取れるのに、子供のMenuが取れないのはなんでなんだろう。

とりあえずなんですけど、VMから直接コマンド飛ばして逃げたが、良くないねこれは。

var hoge = main.Dynamic().DataContext; //MainViewModelのVM
hoge.NavigateCommand.Execute("Page1");

キーイベント関係をどうしよう

ボタンを押すサンプルが転がっているが、キーイベント関連があまりなかった。
このコードで出来たんだけど、いいのかな。何か間違ってないよね。

System.Windows.Forms.SendKeys.SendWait("{ENTER}");
  • テキストボックスのReturnキー押下
  • モーダルで上げたWindowのボタン押下
  • DataGridのセル内キーイベント押下と右クリック
  • DataGridにチェックを付ける

コードでUIが操作できると楽しいので、がんばる。