3

私がいくつかの同様のプロパティを持つクラスを持っていると仮定しましょう:

public string First { get; set; }
public string Second { get; set; }
public string Third { get; set; }

私は自分のテストで同じ方法でそれらをテストしたいのです...だから私はこう書きます:

[Test]
public void TestFirst()
{
    // Asserting strings here
}

3つのテスト(1つは1つ目、1つは2つ目、もう1つは3つ目)の作成を回避する方法はありますか?

のようなものを探している[Values(First, Second, Third)]ので、プロパティを反復処理する1つのテストを作成できます。

乾杯、そして事前に感謝します:)

4

7 に答える 7

2

これはどう:

[TestFixture]
public class Tests
{
    [Test]
    public void Test()
    {
        var obj = new MyClass();

        obj.First = "some value";
        obj.Second = "some value";
        obj.Third = "some value";

        AssertPropertyValues(obj, "some value", x => x.First, x => x.Second, x => x.Third);
    }

    private void AssertPropertyValues<T, TProp>(T obj, TProp expectedValue, params Func<T, TProp>[] properties)
    {
        foreach (var property in properties)
        {
            TProp actualValue = property(obj);
            Assert.AreEqual(expectedValue, actualValue);
        }
    }
}
于 2013-01-15T07:02:01.953 に答える
1

この目的のために式ツリーを使用できるはずです。Expression.PropertyメソッドのMSDNドキュメントを使用して、任意のオブジェクトから名前がT付けられた型プロパティを取得するための次のヘルパーメソッドを作成しました。propertyNameobj

public T InvokePropertyExpression<T>(object obj, string propertyName)
{
    return Expression.Lambda<Func<T>>(Expression.Property(
               Expression.Constant(obj), propertyName)).Compile()();
}

単体テストでこのヘルパーメソッドを使用すると、名前に基づいて関連するプロパティにアクセスできるようになります。たとえば、次のようになります。

[Test, Sequential]
public void Tests([Values("First", "Second", "Third")] string propertyName,
                  [Values("hello", "again", "you")] string expected)
{
    var obj = new SomeClass 
        { First = "hello", Second = "again", Third = "you" };
    var actual = InvokePropertyExpression<string>(obj, propertyName);
    Assert.AreEqual(expected, actual);
}
于 2013-01-15T08:40:37.970 に答える
1

を使用してこのような種類のアサーションを表現する可能性はたくさんありますNUnit.Framework.Constraints.Constraint。さらに、またはを使用する代わりに、またはValuesAttributeを使用TestCaseAttributeしてテストのより多くの入力を記述することができますValuesSoueceAttributeTestCaseSourceAttribute

テスト入力を説明する

を使用して、期待されるプロパティ名とその値を定義しましょうTestCaseSourceAttribute

public IEnumerable TestCasesSourcesAllProperties
{
    get
    {
        yield return new TestCaseData(
            new Tuple<string, string>[] { 
                Tuple.Create("First", "foo"), 
                Tuple.Create("Second", "bar"), 
                Tuple.Create("Third", "boo") } as object)
                    .SetDescription("Test all properties using Constraint expression");
    }
}

単一のテスト内に制約を作成する

これで、1回のテストで3つのプロパティすべての制約を作成できました。

// get test parameters from TestCasesSourcesAllProperties
[TestCaseSource("TestCasesSourcesAllProperties")]
public void ClassUnderTest_CheckAllProperty_ExpectValues(Tuple<string, string>[] propertiesNamesWithValues)
{
    // Arrange
    ClassUnderTest cut = null;

    // Act: perform actual test, here is only assignment
    cut = new ClassUnderTest { First =  "foo", Second = "bar",  Third  = "boo" };

    // Assert
    // check that class-under-test is not null
    NUnit.Framework.Constraints.Constraint expression = Is.Not.Null;

    foreach(var property in propertiesNamesWithValues)
    {
        // add constraint for every property one by one
        expression = expression.And.Property(property.Item1).EqualTo(property.Item2);
    }

    Assert.That(cut, expression);
}

これが完全な例です

欠点

コンフィクションロジック、つまりforeach、テストロジック内

于 2013-01-15T12:24:10.920 に答える
1

これを行うのは簡単ですが、それだけの価値があるかどうか疑問に思います。

方法-上記の回答の多くは機能しますが、新しく作成されたオブジェクトをテストしていると仮定すると、これは最も簡単なようです...

[TestCase("First", "foo"]
[TestCase("Second", 42]
[TestCase("Third", 3.14]
public void MyTest(string name, object expected)
{
    Assert.That(new MyClass(), Has.Property(name).EqualTo(expected));
}

ただし、3つの別々のアサートをテストに入れると、はるかに読みやすくなります...

[Test]
public void MyTest()
{
    var testObject = new MyClass();
    Assert.That(testObject, Has.Property("First").EqualTo("foo"));
    Assert.That(testObject, Has.Property("Second").EqualTo(42));
    Assert.That(testObject, Has.Property("Third").EqualTo(3.14));
}

もちろん、これは、DefaultConstructorInitializesMyClassCorrectlyのように、3つのアサートがすべて1つのテストの一部であることを前提としています。それがあなたがテストしているものではない場合、それはより多くのタイピングを必要としますが、3つのテストはより理にかなっています。確実にする1つの方法は、テストの妥当な名前を思い付くことができるかどうかを確認することです。

チャーリー

于 2013-01-17T02:15:40.567 に答える
0

あなたの答えと助けをありがとう。たくさんのことを学びました。

これが私がやったことです。リフレクションを使用してすべての文字列プロパティを取得してから、値に設定し、値が設定されていることを確認し、nullに設定して、空の文字列(プロパティのゲッターのロジック)が返されることを確認しました。

[Test]
public void Test_AllStringProperties()
{
    // Linq query to get a list containing all string properties
    var string_props= (from prop in bkvm.GetType()
                            .GetProperties(BindingFlags.Public | BindingFlags.Instance)
                      where
                        prop.PropertyType == typeof(string) &&
                        prop.CanWrite && prop.CanRead
                      select prop).ToList();

    string_props.ForEach(p =>{
                                 // Set value of property to a different string
                                 string set_val = string.Format("Setting [{0}] to: \"Testing string\".", p.Name);
                                 p.SetValue(bkvm, "Testing string", null);
                                 Debug.WriteLine(set_val);
                                 // Assert it was set correctly
                                 Assert.AreEqual("Testing string", p.GetValue(bkvm, null));

                                 // Set property to null
                                 p.SetValue(bkvm,null,null);
                                 set_val = string.Format("Setting [{0}] to null. Should yield an empty string.", p.Name);
                                 Debug.WriteLine(set_val);
                                 // Assert it returns an empty string.
                                 Assert.AreEqual(string.Empty,p.GetValue(bkvm, null));
                             }
        );
}

このように、誰かがプロパティを追加しても心配する必要はありません。テストコードを更新しなくても自動的にチェックされるためです(ご想像のとおり、誰もがテストを更新したり書き込んだりするわけではありません:)

このソリューションに関するコメントは歓迎されます。

于 2013-01-21T04:02:15.000 に答える
0

Valuesテストメソッドのパラメータの属性を使用できます。

[Test]
public void MyTest([Values("A","B")] string s)
{
    ...
}

ただし、これは文字列定数に対してのみ機能します(つまり、プロパティの値に対しては機能しません)。

リフレクションを使用して、指定された値からプロパティの値を取得できると思います。たとえば、

[Test]
public void MyTest([Values("A","B")] string propName)
{
    var myClass = new MyClass();
    var value = myClass.GetType().GetProperty(propName).GetValue(myClass, null);

    // test value
}

しかし、それは必ずしも最もクリーンなソリューションではありません。おそらく、各プロパティをテストするメソッドを呼び出すテストを作成できます。

[Test]
public void MyTest()
{
    var myClass = new MyClass();
    MyPropertyTest(myClass.First);
    MyPropertyTest(myClass.Second);
    MyPropertyTest(myClass.Third);
}

public void MyPropertyTest(string value)
{
    // Assert on string
}

ただし、単体テストはまさにそれを実行する必要があるため、このテスト方法は避けるのが最善です-コードのユニットをテストします。各テストに正しい名前が付けられている場合、それはあなたが期待していることだけを記録するのに役立ち、将来簡単に追加することができます。

于 2013-01-15T06:54:21.087 に答える
0

パラメータ化されたテストを記述し、プロパティアクセサをパラメータとして渡すことができます。

例を参照してください。3つのプロパティを持つクラスが次のとおりであると想定します。

public class MyClass
{
    public string First { get; set; }
    public string Second { get; set; }
    public string Third { get; set; }
}

次に、テストは次のようになります。

[TestFixture]
public class MyTest
{
    private TestCaseData[] propertyCases = new[]
        {
            new TestCaseData(
                "First",
                (Func<MyClass, string>) (obj => obj.First),
                (Action<MyClass, string>) ((obj, newVal) => obj.First = newVal)),

            new TestCaseData(
                "Second",
                (Func<MyClass, string>) (obj => obj.Second),
                (Action<MyClass, string>) ((obj, newVal) => obj.Second = newVal)),

            new TestCaseData(
                "Third",
                (Func<MyClass, string>) (obj => obj.Third),
                (Action<MyClass, string>) ((obj, newVal) => obj.Third = newVal))
        };

    [Test]
    [TestCaseSource("propertyCases")]
    public void Test(string description, Func<MyClass, string> getter, Action<MyClass, string> setter)
    {
        var obj = new MyClass();
        setter(obj, "42");

        var actual = getter(obj);

        Assert.That(actual, Is.EqualTo("42"));
    }
}

いくつかの注意事項:
1。未使用の文字列の説明は、テストケースがNUnitテストランナーUIまたはResharperを介して実行されるときに、テストケースを区別するための最初のパラメーターとして渡されます。
2.テトケースは独立しており、Firstプロパティのテストが失敗した場合でも、他の2つのテストが実行されます。
3. NUnitテストランナーUIまたはResharperを介して、1つのテストケースのみを個別に実行できます。

だから、あなたのテストはきれいで乾いている:)

于 2013-01-15T18:17:29.253 に答える