C# 中的动态类型 (3)

dynamic type results

单元测试

对于单元测试,我将使用 xUnit 测试框架。 在 .NET Core 中,您可以使用 dotnet new xunit 命令添加一个测试项目。一个显而易见的问题是模拟和验证动态参数,例如,假设您想验证一个方法调用是否具有动态属性。

要使用 Moq 模拟库,您可以通过 NuGet 添加此依赖项,例如:

dotnet add package Moq –-version 4.10.0

假设您有一个接口,其想法是验证它是否被正确的动态对象调用。

public interface IMessageBus { void Send(dynamic message); }

忽略该接口的实现。这些实现细节对于编写单元测试不是必需的。下面是被测试的系统:

public class MessageService { private readonly IMessageBus _messageBus; public MessageService(IMessageBus messageBus) { _messageBus = messageBus; } public void SendRawJson<T>(string json) { var message = JsonConvert.DeserializeObject<T>(json) as dynamic; _messageBus.Send(message); } }

您可以使用泛型,这样就可以为序列化程序传入动态类型。然后调用 IMessageBus 并发送动态消息。被测试的方法接受一个 string 参数,并使用 dynamic 类型进行调用。

对于单元测试,请将其封装在 MessageServiceTests 类中。首先初始化 Mock 和被测试的服务:

public class MessageServiceTests { private readonly Mock<IMessageBus> _messageBus; private readonly MessageService _service; public MessageServiceTests() { _messageBus = new Mock<IMessageBus>(); _service = new MessageService(_messageBus.Object); } }

使用 Moq 库中的 C# 泛型来模拟 IMessageBus,然后使用 Object 属性创建一个模拟实例。在所有的单元测试中私有实例变量都很有用,高可重用性的私有实例增加了类的内聚性。

使用 Moq 验证调用,一种直观的方式是尝试这么做:

_messageBus.Verify(m => m.Send(It.Is<ExpandoObject>(o => o != null && (o as dynamic).a == 1)));

但是,遗憾的是,您将看到这样的错误消息:“表达式树不能包含动态操作。” 这是因为 C# lambda 表达式无法访问 DLR,它期望一个来自 CLR 的类型,这使得此动态参数难以验证。记得您的训练,利用您的“代码感”来解决这个问题。

要处理诸如类型之间不一致的问题,请使用 Callback 方法:

dynamic message = null; _messageBus.Setup(m => m.Send(It.IsAny<ExpandoObject>())).Callback<object>(o => message = o);

请注意,Callback 方法将类型转换为 System.Object。因为所有类型都继承自 object 类型,所以可以将其赋值为 dynamic 类型。C# 可以把此 lambda 表达式中的 object 拆箱成 dynamic message。

是时候为 ExpandoObject 类型编写一个漂亮的单元测试了。使用 xUnit 作为测试框架,您将看到带有 Fact 属性的方法。

[Fact] public void SendsWithExpandoObject() { // arrange const string json = "{\"a\":1}"; dynamic message = null; _messageBus.Setup(m => m.Send(It.IsAny<ExpandoObject>())).Callback<object>(o => message = o); // act _service.SendRawJson<ExpandoObject>(json); // assert Assert.NotNull(message); Assert.Equal(1, message.a); }

使用 DynamicObject 类型进行测试,重用您之前看到的 TypedDynamicJson:

[Fact] public void SendsWithDynamicObject() { // arrange const string json = "{\"a\":1,\"b\":\"1\"}"; dynamic message = null; _messageBus.Setup(m => m.Send(It.IsAny<TypedDynamicJson<long>>())).Callback<object>(o => message = o); // act _service.SendRawJson<TypedDynamicJson<long>>(json); // assert Assert.NotNull(message); Assert.Equal(1, message.a); Assert.Equal("a", string.Join(",", message.GetDynamicMemberNames())); }

使用 C# 泛型,您可以在重用代码的同时转换序列化程序的动态类型。Moq 中的 Callback 方法允许您在两种类型系统之间进行必要的跳转。拥有一个优雅的类型层次结构和一个共同的父类成为了一个救星。

Using 语句

下面的 using 语句是代码示例的一部分:

System: CLR 的基础类型,例如 Object 和 Console

System.Collections.Generic: 可枚举类型,例如 IDictionary

System.Dynamic: DLR 的动态类型,例如 ExpandoObject 和 DynamicObject

Newtonsonft.Json: JSON 序列化程序

Moq: 模拟库

Xunit: 测试框架

总结

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpyjgz.html