如何在ASP.NET Core 3.0中编写涉及DateTime Json序列化的集成测试



我正在为ASP.NET Core 3.0中的Api控制器编写集成测试。该测试针对的是一个以实体列表作为响应的路由。当我试图对响应内容进行断言时,DateTime属性的序列化方式存在差异。

我尝试在测试中使用自定义JsonConverter:

public class DateTimeConverter : JsonConverter<DateTime>
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return DateTime.Parse(reader.GetString());
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString("yyyy-MM-ddThh:mm:ss.ffffff"));
}
}

问题是,这个转换器不会截断尾随的零,而实际的响应会截断。因此,该测试失败的几率为十分之一。

这是失败的测试:

[Fact]
public async Task GetUsers()
{
using var clientFactory = new ApplicationFactory<Startup>();
using var client = clientFactory.CreateClient();
using var context = clientFactory.CreateContext();
var user1 = context.Users.Add(new User()).Entity;
var user2 = context.Users.Add(new User()).Entity;
context.SaveChanges();
var users = new List<User> { user1, user2 };
var jsonSerializerOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
var serializedUsers = JsonSerializer.Serialize(users, jsonSerializerOptions);
var response = await client.GetAsync("/users");
var responseBody = await response.Content.ReadAsStringAsync();
Assert.Equal(serializedUsers, responseBody);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}

我本以为测试会通过,但我却得到了这个错误:

Error Message:
Assert.Equal() Failure
↓ (pos 85)
Expected: ···1-05T22:14:13.242771-03:00","updatedAt"···
Actual:   ···1-05T22:14:13.242771","updatedAt"···

我没有在控制器的实际实现中配置任何序列化选项。

如何正确实现此集成测试?有没有一种简单的方法可以使用实际控制器的相同选项在测试中序列化列表?

1。使用UTC时间戳

我真的鼓励你保持UTC的时间戳。

var x = new { UpdatedAtUtc = DateTime.UtcNow };
Console.WriteLine(JsonSerializer.Serialize(x));

产生

{"UpdatedAtUtc":"2019-11-06T02:41:45.4610928Z"}

2.使用转换器

var x = new { UpdatedAt = DateTime.Now };
JsonSerializerOptions options = new JsonSerializerOptions();
options.Converters.Add(new DateTimeConverter());
Console.WriteLine(JsonSerializer.Serialize(x, options));
{"UpdatedAt":"2019-11-06T12:50:48.711255"}

3.使用DateTimeKind.Unspecified

class X { public DateTime UpdatedAt {get;set;}}
public static void Main()
{
var localNow = DateTime.Now;
var x = new X{ UpdatedAt = localNow };
Console.WriteLine(JsonSerializer.Serialize(x));
x.UpdatedAt = DateTime.SpecifyKind(localNow, DateTimeKind.Unspecified);
Console.WriteLine(JsonSerializer.Serialize(x));

产生

{"UpdatedAt":"2019-11-06T12:33:56.2598121+10:00"}
{"UpdatedAt":"2019-11-06T12:33:56.2598121"}

Btw。您应该在测试代码和测试代码中使用相同的Json选项。


关于微秒和DateTimeKind的注释

随着测试的深入,您可能会发现放入数据库的对象与从数据库检索到的对象之间的时间戳不匹配。

根据您的设置,DateTimes可能会作为LocalUnspecified从数据库中检索(即使您将Utc放入数据库中(,并且您可能会丢失一些精度(数据库列将只存储其最大恢复值,可能为毫秒(。

您想要做的是获得DateTime"往返"表示,这可以通过使用:来完成

https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings#Roundtrip

var d = DateTime.Now.ToString("o");

如果您仍然发现使两种DateTime格式相同的问题,那么您可以使用"System.DateTime.Kind"属性。

https://learn.microsoft.com/en-us/dotnet/api/system.datetime.kind?view=netframework-4.8

下面是我做的一个简单的例子,你可以在线运行:

https://dotnetfiddle.net/ccGSEO

最新更新