简体   繁体   English

如何对返回FUNC的C#函数进行单元测试 <something> ?

[英]How do I unit test a C# function which returns a Func<something>?

I have a class containing a method which returns a Result object which contains a property of type Func. 我有一个包含一个方法的类,该方法返回一个Result对象,其中包含Func类型的属性。

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

How do I write a unit test assertion regarding the content of this Func? 如何编写有关此Func内容的单元测试断言? The following obviously does not work, because the compiler generates two different methods for the lambda: 以下显然不起作用,因为编译器为lambda生成了两种不同的方法:

// 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));

I'm guessing that I could do this by using expression trees instead, but... is there a way to avoid doing so? 我猜想我可以通过使用表达式树来做到这一点,但是...有避免的方法吗? I'm using NUnit 2.5. 我正在使用NUnit 2.5。

EDIT: There are no other identifying fields in the Result object. 编辑:结果对象中没有其他标识字段。 It is intended to be a way of invoking the next object/method based on a decision made in the current object/method. 它旨在成为基于当前对象/方法中做出的决定调用下一个对象/方法的方式。

Why not invoke the Func and compare the returned values? 为什么不调用Func并比较返回的值?

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

EDIT: I see that the Result class does not have any identity. 编辑:我看到结果类没有任何标识。 I guess you have some other fields in the Result class that define the identity of the Result and can be used to determine if two results are equal. 我猜您在Result类中还有一些其他字段可以定义Result的身份,并可以用来确定两个结果是否相等。

I'm not aware of an easy way to look inside a lambda (other than using expression trees as you said) but it is possible to compare delegates if they're assigned a method group instead. 我不知道一种在lambda内部查找的简单方法(除了您所说的使用表达式树之外),但是如果为委托分配了方法组 ,则可以比较委托。

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));

The above example does not work if you use lambdas since they are compiled to different methods. 如果使用lambda,则上面的示例不起作用,因为它们被编译为不同的方法。

如果Func<Result>始终返回相同的结果,则可以测试该函数返回的对象。

Well it appears that unit testing the contents of a Func goes beyond the normal range of unit testing. 看起来,对Func的内容进行单元测试超出了单元测试的正常范围。 A Func represents compiled code, and therefore can not be further inspected without resorting to parsing MSIL. Func表示已编译的代码,因此,如果不诉诸MSIL的解析,则无法对其进行进一步检查。 In this situation, it is therefore necessary to fall back on delegates and instantiated types (as suggested by Nathan Baulch), or to use expression trees instead. 因此,在这种情况下,有必要回落到委托和实例化类型上(如Nathan Baulch所建议),或者使用表达式树代替。

My expression tree equivalent below: 我的表达式树等效如下:

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

with the unit testing as follows: 单元测试如下:

// 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"));

Note that there is some inherent fragility to this test as it implies the structure of the expression, as well as its behaviour. 请注意,此测试存在一些固有的脆弱性,因为它隐含了表达式的结构及其行为。

If I understand the issue properly the NextAction may or may not have a different lambda implementation, which is what needs testing. 如果我正确理解了该问题,则NextAction可能有也可能没有其他lambda实现,这需要测试。

In the example below I compare the methods IL bytes. 在下面的示例中,我比较了方法IL字节。 Using reflection, get the method info and IL bytes from the body in an array. 使用反射,从数组的正文中获取方法信息和IL字节。 If the byte arrays match, the lambda's are the same. 如果字节数组匹配,则lambda相同。

There are lots of situations that this wont handle, but if it's just question of comparing two lambda's that should be exactly the same, this will work. 在很多情况下,这是无法处理的,但是如果只是比较应该完全相同的两个lambda的问题,那么这将起作用。 Sorry it's in MSTest :) 抱歉,它在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; }
        }
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何对返回函数的字典进行单元测试? - How to unit test a dictionary which returns a func? 如何为C#Azure功能编写单元测试? - How do I write Unit Test for C# Azure Function? 当 function 必须写入标准输出时,我如何对 C# function 进行单元测试 - How do I unit test a C# function when that function must write to stdout 如何将单元测试应用于需要动态用户输入的C#功能? - How do I apply unit testing to C# function which requires user input dynamically? 如何正确使用 C# 单元测试 FakeHttpContext? - How do I use C# Unit test FakeHttpContext correctly? 我如何在c#中对单元测试的URL有效? - How do I Unit test is URL valid in c#? 如何对该C#方法进行单元测试,该方法从api返回团队列表? - How to unit test this C# method which returns List of teams from an api? 如何声明返回相同类型的Func委托的Func委托? - How do I declare a Func Delegate which returns a Func Delegate of the same type? 如何使用 Ms unit project c# 代码为逻辑应用程序步骤编写单元测试? - How do I write unit test for logic apps steps using Ms unit project c# code? 如何在C#中调用带参数并返回struct的dll C ++函数? - How to call dll C++ func which takes parameter and returns struct in C#?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM