C#Xamarin.FormsでPrismでMVVM実装#04 ViewとViewModelに分ける理由

当サイトではアフィリエイト広告を利用しています。

PrismでXamarin.Formsする方法

前回はMVVMアーキテクチャーがViewとViewModelに分けて,データバインディングを行う仕組みについて解説しましたが,そもそもなぜViewとViewModelに分ける必要があるのか?という話をしていきたいと思います。

MVVMができる前の実装の話

MVVMという実装パターンが世の中に広まるまでは,ボタンクリックイベントの中に,コントロールのTextプロパティに値を設定する処理を書いていました。ボタンクリックイベントの中に直接書かれていなかったとしても,そこから呼び出されるメソッドに書かれていても同じことです。要は画面プログラムの中で,画面への描画と,値の設定や判断などの処理を両方やっていたのです。

ボタンクリックイベントに処理を書いてみる

ボタンクリックイベントに画面の項目を制御する処理を記述すると何がいけないのかを,実演で解説していきたいと思います。

まず,MainPage.xamlにLabelとButtonをひとつずつ置きます。

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="BlankApp2.Views.MainPage">

    <StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
        <Label Text="Welcome to Xamarin Forms and Prism!" />
        <Label Text="AAA" x:Name="MyLabel"/>
        <Button Text="button a" Clicked="Button_Clicked"/>
    </StackLayout>

</ContentPage>
  • StackLayoutをおいて,そのまま垂直方向にコントロールを設置します。
  • LabelのTextにはAAA
  • 名前は「MyLabel」としています。
  • ボタンのTextには「button a」
  • Clickedイベントを記述します。「Clicked=」くらいまで打ち込むと,VisualStudioが新しいイベントを作成自動生成するかどうかを確認してくるので,そのまま自動生成させます。
using System;
using Xamarin.Forms;

namespace BlankApp2.Views
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
        }

        private void Button_Clicked(object sender, EventArgs e)
        {
            ////テストできない
            MyLabel.Text = "BBB";
        }
    }
}

MainPageのコードビハインド側に自動生成されたボタンクリックイベント「Button_Clicked」でMyLabelのテキストに“BBB”と設定するプログラミングをします。 この状態で一度実行してみます。

「BUTTON A」をクリックするとラベルが「BBB」に変化する事が確認できます。

この処理をテストコードでカバーできるのか?

ここまで見てきたシンプルな処理,「起動時はラベルにAAAと表示され,ボタンを押したら,BBBに変化する」という動作は,テストコードでどのようにテストしたらよいか?という問題があります。

テストコードとはMSTestやNUnit等の,プログラムコードを外部から呼び出し,メソッド等の自動テストをするツールの事です。今回はMSTestを使用します。

テストプロジェクトの追加

実際にテストプロジェクトを作成していきます。

ソリューションに新しいテストプロジェクトを追加します。

ソリューションを右クリックして「追加」,「新しいプロジェクトを追加」画面の検索窓に「mstest」と入力して出てくる,MsTestテストプロジェクト(.NET Core)を選択します(VBのやつがあるので間違えないようにC#の物を選んでください)。

プロジェクト名はデフォルトのままで「作成」ボタンを押下します。

追加すると次のように,「UnitTestProject1」というテストプロジェクトが作成されます。

「UnitTestProject1」の「依存関係」を右クリックして「参照の追加」を選択します。

「BlancApp1」にチェックを入れて「OK」ボタンを押下します。

これでテストプロジェクトから「BlancApp1」プロジェクトが見えるようになりました。

テストコードの記述

テストプロジェクトにデフォルトで作成されているUnitTest1.csにテストコードを書きます。今回の実装は,起動時にラベルのテキストがAAAで,ボタンを押したらBBBになるという仕様の為,それを確認するテストコードにします。テストメソッドは日本語で書いていきます。最初は違和感があるかもしれませんが,テストメソッドが外部から呼ばれないことと,何をテストしているのかが,単体テスト仕様書の感じで理解できるようになるため,このような日本語でテストメソッド名にするやり方を推奨します。

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace UnitTestProject1
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void 起動時はAAAでボタンを押したらBBBになる()
        {
        }
    }
}

ここからMainPageの処理をテストしたいのですが,MainPageのボタンクリックイベントも,LabelのTextプロパティもすべてPrivateで宣言されているため,外部からテストするのは困難です。 テストコードでメソッドをテストする場合,計算などを行うクラスで,クラスの中だけで完結しているものなら非常にテストはやりやすいのですが,画面の描画をするようなものや,ボタンをクリックする等のイベントがあるクラスのテストというのは非常にやりにくいという特性があります。 それらの問題を解決するために,MVVMパターンでは,画面の描画を行うViewの部分と,設定や判断等を行うロジック部分をViewModelに記述するスタイルになっています。テストしづらいViewはテストせずに,ViewModelに対してテストコードを書くことで,ロジックの正当性を検証できるのです。次回はそのViewとViewModelに分けたことで,うまくテストコードが書けるかどうかを検証していきます。