Возможно ли это в C #? - PullRequest
       33

Возможно ли это в C #?

8 голосов
/ 07 декабря 2009

У меня есть метод расширения для тестирования, поэтому я могу сделать это:

var steve = new Zombie();
steve.Mood.ShouldBe("I'm hungry for brains!");

Метод расширения:

public static void ShouldBe<T>(this T actual, T expected)
{
    Assert.That(actual, Is.EqualTo(expected));
}

Это показывает:

Expected: "I'm hungry for brains!"
But was:  "I want to shuffle aimlessly"

Есть ли какой-нибудь хак, который я могу осуществить, чтобы получить имя свойства "BrainsConsumed" из моего метода расширения? Бонусными баллами будет переменная экземпляра и введите Zombie.

UPDATE:

Новый mustBe:

public static void ShouldBe<T>(this T actual, T expected)
{
    var frame = new StackTrace(true).GetFrame(1);
    var fileName = frame.GetFileName();
    var lineNumber = frame.GetFileLineNumber() - 1;
    var code = File.ReadAllLines(fileName)
        .ElementAt(lineNumber)
        .Trim().TrimEnd(';');

    var codeMessage = new Regex(@"(^.*)(\.\s*ShouldBe\s*\()([^\)]+)\)").Replace(code, @"$1 should be $3");

    var actualMessage = actual.ToString();
    if (actual is string)
        actualMessage = "\"" + actual + "\"";

    var message = string.Format(@"{0} but was {1}", codeMessage, actualMessage);

    Assert.That(actual, Is.EqualTo(expected), message);
}

и это распечатывает:

steve.Mood should be "I'm hungry for brains!" but was "I want to shuffle aimlessly"

Спасибо всем, особенно. Мэтт Дотсон, это круто. Кстати, не кормите шелковистых троллей.

Ответы [ 5 ]

3 голосов
/ 07 декабря 2009

Вы можете получить код, если это отладочная сборка, используя некоторые из классов диагностики. Учитывая, что это для модульных тестов, DEBUG, вероятно, является разумным.

public static void ShouldBe<T>(this T actual, T expected)

{

var frame = new StackTrace(true).GetFrame(1);
var fileName = frame.GetFileName();
var lineNumber = frame.GetFileLineNumber() - 1;

string code = File.ReadLines(fileName).ElementAt(lineNumber).Trim();

Debug.Assert(actual.Equals(expected), code);

}

Для вашего примера, code = "steve.BrainsConsumed.ShouldBe (0);"

Очевидно, что вы должны добавить некоторую проверку ошибок в этот код, и вы могли бы сделать это быстрее, не читая все строки в файле.

3 голосов
/ 07 декабря 2009

Лучшее, что я могу сделать:

steve.Property(p => p.BrainsConsumed).ShouldBe(0);

или

steve.ShouldBe(p => p.BrainsConsumed, 0);

или

Assert.AreEqual(() => steve.BrainsConsumed, 0);

Re:

Бонусными баллами будет переменная экземпляра

Используя Expression<Func<TSource, TValue>> (или просто Expression<Func<T>>), вы можете довольно легко получить имя и значение свойства. Я приведу пример для середины - обратите внимание, что для первого требуется дополнительный тип для DSL, но ничего тяжелого:

public static class Test
{
    public static void AssertEqual<TSource, TValue>(
        this TSource source,
        Expression<Func<TSource, TValue>> selector,
        TValue expected)
        where TSource : class
    {
        TValue value = selector.Compile()(source);
        string paramName = selector.Parameters[0].Name;

        System.Diagnostics.Debug.Assert(
            EqualityComparer<TValue>.Default.Equals(value, expected),
            typeof(TSource) + " " + paramName + ": " +
                value + " doesn't match expected " + expected);
    }
}

Или немного лучше сообщение:

public class Zombie
{
    public int BrainsConsumed { get; set; }
    static void Main() {
        Zombie steve = new Zombie { BrainsConsumed = 2 };
        Test.ShouldBeEqual(() => steve.BrainsConsumed, 0);
    }

}
public static class Test
{
    static string GetName(Expression expr)
    {
        if (expr.NodeType == ExpressionType.MemberAccess)
        {
            var me = (MemberExpression)expr;
            string name = me.Member.Name, subExpr = GetName(me.Expression);
            return string.IsNullOrEmpty(subExpr)
                ? name : (subExpr + "." + name);
        }
        return "";
    }
    public static void ShouldBeEqual<TValue>(
        Expression<Func<TValue>> selector,
        TValue expected)
    {
        TValue value = selector.Compile()();

        string name = GetName(selector.Body);

        System.Diagnostics.Debug.Assert(
            EqualityComparer<TValue>.Default.Equals(value, expected),
            typeof(TValue) + " " + name + ": " +
                value + " doesn't match expected " + expected);
    }
}
1 голос
/ 07 декабря 2009

Нет, я не думаю, что вы можете.

Предположим, что BrainsConsumed является целым числом (что выглядит вероятным). В этом случае параметр передается по значению - все, что вы получаете, это копия целого числа, которое вы тестируете. У него нет имени, кроме имени в локальной области (фактическое).

Этот похожий вопрос может прояснить:

Поиск имени переменной, переданной функции

0 голосов
/ 07 декабря 2009

Это позволит вам протестировать его, но не даст вам название метода.

Вы можете иметь это расширение:

public static void ShouldBe<T>(this Func<T> func, T expected)
{
    Assert.AreEqual(func(), expected);
}

со следующим использованием:

((Func<int>)Program.TestMethod).ShouldBe(2);
0 голосов
/ 07 декабря 2009

К сожалению, в этой позиции вы не сможете получить название собственности на этом этапе. Проблема в том, что вы передаете значение поля BrainsConsumed, и в этот момент просто нет ссылки на зомби (если говорить о вашем методе, то это int, и он не может сработать там, где изначально появился int). от).

Лучшее, что я могу придумать для вас, это то, что Environment.StackTrace будет содержать соответствующую информацию, так как вы назвали steve.BrainsConsumed 1 шаг вверх по стеку (рекомендуется только если вы пытаетесь это сделать получить некоторое представление о том, что не удалось в ваших модульных тестах - не для того, чтобы на самом деле обойти стек в обычном потоке программы).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...