2

ViewModel を引数として取るヘルパー メソッドをテストしたいと考えています。私が抱えている問題は、テストでは、ViewModel が使用するすべてのモデルをインスタンス化して割り当てる必要があるように見えることです。以下に示す例では、数が少ないためそれほど大したことではありませんが、実際に取り組んでいる VM には大量の VM があります。すべてのオブジェクトを作成して割り当てる必要がないように、これを行う他の方法はありますか?

説明用のコード例...

モデル

public class Meal
{
    public int MealID { get; set; }

    public string Title { get; set; }
    public decimal Cost { get; set; }
}

public class Beverage
{
    public int BeverageID { get; set; }

    public string Title { get; set; }
    public decimal Cost { get; set; }
}

public class Desert
{
    public int DesertID { get; set; }

    public string Title { get; set; }
    public decimal Cost { get; set; }
}

ビューモデル

public class DinnerViewModel
{
    public Meal Meal { get; set; }
    public Beverage Beverage { get; set; }
    public Desert Desert { get; set; }

    public decimal SalesTax { get; set; }
    public bool SeniorDiscount { get; set; }
}

ヘルパー

public class Calculator
{
    public decimal Total(DinnerViewModel dvm)
    {
        decimal subtotal = dvm.Meal.Cost + dvm.Beverage.Cost + dvm.Desert.Cost;

        if (dvm.SeniorDiscount)
        {
            subtotal = subtotal - (subtotal * 0.1M);
        }

        return subtotal + (subtotal * dvm.SalesTax);
    }
}

テスト

[TestMethod]
public void CalculatorReturnsCorrectTotalForNonSenior()
{
    DinnerViewModel dvm = new DinnerViewModel();
    dvm.Meal.Cost = 7M;
    dvm.Beverage.Cost = 1M;
    dvm.Desert.Cost = 2M;
    dvm.SalesTax = 0.08M;
    dvm.SeniorDiscount = false;

    Calculator calc = new Calculator();

    decimal expected = 10.80M;
    decimal actual = calc.Total(dvm);

    Assert.AreEqual(expected, actual, "The actual value does not match the expected value.");
}

これにより、「NullReferenceException」エラーが発生します。先ほど言ったように、必要なオブジェクトを作成して割り当てることができます...

[...]
Meal meal = new Meal();
dvm.Meal = meal;
dvm.Meal.Cost = 7M;
[...]

...そして、すべてが完了すると、テストは成功しますが、大規模な VM ではかなりの作業のように思えます。これを簡単にするために何かできることがあると感じています。

4

2 に答える 2

2

Meal、Beverage、および Desert のインスタンスは、DinnerViewModel のインスタンス化時に対応するプロパティに割り当てられないため、NullReferenceException が表示されます。したがって、これらのプロパティはすべてnull. これらのプロパティにアクセスする前に、新しいオブジェクトを作成して割り当てる必要があります。ビューモデルのコンストラクターを使用できます:

public class DinnerViewModel
{
    public DinnerViewModel()
    {
        Meal = new Meal();
        Beverage = new Beverage();
        Desert = new Desert();
    }

    public Meal Meal { get; set; }
    public Beverage Beverage { get; set; }
    public Desert Desert { get; set; }

    public decimal SalesTax { get; set; }
    public bool SeniorDiscount { get; set; }
}

また、テスト用のスタブを返すヘルパー メソッドを作成することも好きです。重複を取り除き、テストを明確にします。

private DinnerViewModel CreateTenDollarsDinner()
{
    return new DinnerViewModel {
        Meal = new Meal { Cost = 7M },
        Beverage = new Beverage { Cost = 1M },
        Desert = new Desert { Cost = 2M },
        SalesTax = 0.08M,
        SeniorDiscount = false
    };
}

[TestMethod]
public void CalculatorReturnsCorrectTotalForNonSenior()
{
    DinnerViewModel dvm = CreateTenDollarsDinner();    
    Calculator calc = new Calculator();
    Assert.AreEqual(10.80M, calc.Total(dvm));
}
于 2013-10-24T15:23:36.620 に答える
0

モデル (Meal、Beaverage、Dessert) も ViewModel であるか、DinnerViewModel が実際には ViewModel ではありません。

ViewModel には、View に直接使用できる値を提供するという目的があります。

単体テストの例に基づいて、Calculator.Total ヘルパー メソッドを直接 DinnerViewModel に配置し、計算された合計値をプロパティとしてビューに公開する方が適切です。

public class DinerViewModel
{
    public Meal Meal { get; set; }
    public Beverage Beverage { get; set; }
    public Desert Desert { get; set; }

    public decimal SalesTax { get; set; }
    public bool SeniorDiscount { get; set; }

    public decimal TotalCostOfDinner
    {
        get
        {
            decimal subtotal = 0M;
            if(Meal != null) subtotal += Meal.Cost;
            if(Beverage != null) subtotal += Beverage.Cost;
            if(Desert != null) subtotal += Desert.Cost;

            if (SeniorDiscount) subtotal -= subtotal * 0.1M;

            return subtotal + (subtotal * SalesTax);
        }
    }
}

これで、View は、DinnerViewModel から直接、正しい TotalCostOfDinner を取得できます。

次に単体テストを行います。

[TestMethod]
public void TotalCostOfDinnerReturnsCorrectTotalForNonSenior()
{
    DinnerViewModel dvm = new DinnerViewModel
    {
        Meal = new Meal { Cost = 7M },
        Beverage = new Beverage { Cost = 1M },
        Desert = new Desert { Cost = 2M },
        SalesTax = 0.08M,
        SeniorDiscount = false
    };


    decimal expected = 10.80M;
    decimal actual = dvm.TotalCostOfDinner;

    Assert.AreEqual(expected, actual, "The actual value does not match the expected value.");
}
于 2013-10-24T16:08:14.120 に答える