Как делают меня модульный тест функция C#, которая возвращает Func <что-то>?

У меня есть класс, содержащий метод, который возвращает Объект результата, который содержит свойство типа Func.

class Result {
   public Func<Result> NextAction { get; set; }
}

Как я пишу утверждение модульного теста относительно содержания этого Func? Следующее, очевидно, не работает, потому что компилятор генерирует два различных метода для лямбды:

// Arrange
ListController controller = new ListController(domain);
// Act
Result actual = controller.DefaultAction();
// Assert
Func<Result> expected = () => new ProductsController(domain).ListAction();
Assert.That(actual.NextAction, Is.EqualTo(expected));

Я предполагаю, что мог сделать это при помощи деревьев выражений вместо этого, но... являюсь там способом постараться не делать так? Я использую NUnit 2.5.

Править: В Объекте результата нет никаких других полей идентификации. Это предназначается, чтобы быть способом вызвать следующий объект/метод на основе решения, принятого в текущем объекте/методе.

8
задан goofballLogic 22 March 2010 в 12:12
поделиться

5 ответов

Что ж, похоже, что модульное тестирование содержимого Func выходит за рамки обычного диапазона модульного тестирования. Функция Func представляет скомпилированный код и поэтому не может быть дополнительно проверена без обращения к синтаксическому анализу MSIL. Поэтому в этой ситуации необходимо прибегнуть к делегатам и экземплярам типов (как предлагает Натан Баулч) или вместо этого использовать деревья выражений.

Мой эквивалент дерева выражений ниже:

class Result {
   public Expression<Func<Result>> NextAction { get; set; }
}

со следующим модульным тестированием:

// Arrange
ListController controller = new ListController(domain);
// Act
Result actual = controller.DefaultAction();
// Assert
MethodCallExpression methodExpr = (MethodCallExpression)actual.NextAction.Body;
NewExpression newExpr = (NewExpression)methodExpr.Object;
Assert.That(newExpr.Type, Is.EqualTo(typeof(ProductsController)));
Assert.That(methodExpr.Method.Name, Is.EqualTo("ListAction"));

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

0
ответ дан 6 December 2019 в 00:06
поделиться

Почему бы не вызвать Func и не сравнить возвращенные значения?

var actualValue = actual.NextAction();
var expectedValue = expected();
Assert.That(actualValue, Is.EqualTo(expectedValue));

РЕДАКТИРОВАТЬ: Я вижу, что класс Result не имеет личность. Я предполагаю, что у вас есть некоторые другие поля в классе Result, которые определяют идентичность Result и могут использоваться для определения равенства двух результатов.

2
ответ дан 6 December 2019 в 00:06
поделиться

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

var result1 = new Result {
    NextAction = new ProductsController(domain).ListAction };
var result2 = new Result {
    NextAction = new ProductsController(domain).ListAction };

//objects are different
Assert.That(result1, Is.Not.EqualTo(result2));

//delegates are different
Assert.That(result1.NextAction, Is.Not.EqualTo(result2.NextAction));

//methods are the same
Assert.That(result1.NextAction.Method, Is.EqualTo(result2.NextAction.Method));

Приведенный выше пример не работает, если вы используете лямбды, поскольку они скомпилированы для разных методов.

2
ответ дан 6 December 2019 в 00:06
поделиться

Если вы Func всегда возвращаете один и тот же результат, вы можете проверить, какой объект возвращается функцией.

0
ответ дан 6 December 2019 в 00:06
поделиться

Если я правильно понимаю проблему, NextAction может иметь или не иметь другую реализацию лямбда-выражения, что и требует тестирования.

В приведенном ниже примере я сравниваю байты IL методов. Используя отражение, получите информацию о методе и байты IL из тела в массиве. Если байтовые массивы совпадают, лямбда одинаковы.

Есть много ситуаций, с которыми эта функция не справляется, но если это просто вопрос сравнения двух лямбда-выражений, которые должны быть совершенно одинаковыми, это сработает. Извините, это в MSTest :)

using System.Reflection;
....


    [TestClass]
    public class Testing
    {
        [TestMethod]
        public void Results_lambdas_match( )
        {
            // Arrange 
            ListController testClass = new ListController( );
            Func<Result> expected = ( ) => new ProductsController( ).ListAction( );
            Result actual;
            byte[ ] actualMethodBytes;
            byte[ ] expectedMethodBytes;

            // Act 
            actual = testClass.DefaultAction( );

            // Assert
            actualMethodBytes = actual.NextAction.
                Method.GetMethodBody( ).GetILAsByteArray( );
            expectedMethodBytes = expected.
                Method.GetMethodBody( ).GetILAsByteArray( );

            // Test that the arrays are the same, more rigorous check really should
            // be done .. but this is an example :)
            for ( int count=0; count < actualMethodBytes.Length; count++ )
            {
                if ( actualMethodBytes[ count ] != expectedMethodBytes[ count ] )
                    throw new AssertFailedException(
                       "Method implementations are not the same" );
            }
        }
        [TestMethod]
        public void Results_lambdas_do_not_match( )
        {
            // Arrange 
            ListController testClass = new ListController( );
            Func<Result> expected = ( ) => new OtherController( ).ListAction( );
            Result actual;
            byte[ ] actualMethodBytes;
            byte[ ] expectedMethodBytes;
            int count=0;

            // Act 
            actual = testClass.DefaultAction( );

            // Assert
            actualMethodBytes = actual.NextAction.
                Method.GetMethodBody( ).GetILAsByteArray( );
            expectedMethodBytes = expected.
                Method.GetMethodBody( ).GetILAsByteArray( );

            // Test that the arrays aren't the same, more checking really should
            // be done .. but this is an example :)
            for ( ; count < actualMethodBytes.Length; count++ )
            {
                if ( actualMethodBytes[ count ] != expectedMethodBytes[ count ] )
                    break;
            }
            if ( ( count + 1 == actualMethodBytes.Length ) 
                && ( actualMethodBytes.Length == expectedMethodBytes.Length ) )
                throw new AssertFailedException(
                    "Method implementations are the same, they should NOT be." );
        }

        public class Result
        {
            public Func<Result> NextAction { get; set; }
        }
        public class ListController
        {
            public Result DefaultAction( )
            {
                Result result = new Result( );
                result.NextAction = ( ) => new ProductsController( ).ListAction( );

                return result;
            }
        }
        public class ProductsController
        {
            public Result ListAction( ) { return null; }
        }
        public class OtherController
        {
            public Result ListAction( ) { return null; }
        }
    }
0
ответ дан 6 December 2019 в 00:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: