首页 > 解决方案 > C# - Moq - System.Text.Json 自定义 JsonConverter - 如何模拟调用接受 Ref Struct 参数的方法?

问题描述

我是一个新手,试图学习使用 xUnit 和 Moq 进行测试。

我正在为自定义Json.Text.Json.Serialization.JsonConverter编写测试并遇到了绊脚石。

JsonConverter的抽象Read方法接受一个 ref struct Utf8JsonReader。这在我的自定义转换器类中被覆盖。

我的自定义转换器类创建了一个具有接受方法的解析模型(请参阅下面的 IVisitorElement)。这会将反序列化责任移交给访问者实例(参见下面的 IVisitor),以将 json 反序列化到解析模型中。

自定义转换器类

using System;
using System.Text.Json;
using System.Text.Json.Serialization;

using Microsoft.Extensions.Logging;

using WebApp.Data.Serializers.Converters.Visitors;

namespace WebApp.Data.Serializers.Converters
{
    public sealed class MotionDetectionConverter : JsonConverter<MotionDetection>
    {
        private readonly ILogger _log;
        private readonly IVisitorElement<MotionDetection> _parseModel;
        private IVisitor _visitor;
        
        public MotionDetectionConverter(
            IVisitor visitor, 
            IVisitorElement<MotionDetection> parseModel,
            ILogger<MotionDetectionConverter> logger)
        {
            _log = logger ?? throw new ArgumentNullException(nameof(logger));
            _visitor = visitor ?? throw new ArgumentNullException(nameof(visitor));
            _parseModel = parseModel ?? throw new ArgumentNullException(nameof(parseModel));
        }

        public override MotionDetection Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            _parseModel.accept(_visitor, ref reader, options);

            _log.LogInformation("\n{0}\n", _parseModel);

            return _parseModel.ToModel();
        }

        public override void Write(Utf8JsonWriter writer, MotionDetection motionDetection, JsonSerializerOptions options) =>
                throw new NotImplementedException("Serialization of MotionDetection objects is not implemented");
    }
}

访客元素接口

    public interface IVisitorElement<ModelType>
    {
        void accept(IVisitor visitor, ref Utf8JsonReader reader, JsonSerializerOptions options);
        ModelType ToModel();
    }

访客界面

    public interface IVisitor {
        void DeserializeMotionDetection(ParsedMotionDetection target, ref Utf8JsonReader reader, JsonSerializerOptions options);
        void DeserializeMotionInfo(ParsedMotionInfo target, ref Utf8JsonReader reader, JsonSerializerOptions options);
        void DeserializeMotionLocation(ParsedLocation target, ref Utf8JsonReader reader, JsonSerializerOptions options)
    }

测试到现在...

        [Fact(Skip="How to mock a method that accepts a ref struct parameter?")]
        public void MotionDetectionConverter_Deserialize_UnitTest()
        {
            MotionDetection expected = CreateMotionDetection();
            Utf8JsonReader reader = CreateUtf8JsonReader();

            var visitorMock = new Mock<IVisitor>();
            var parsedDetectionModelMock = new Mock<IVisitorElement<MotionDetection>>();
            var parsedInfoModelMock = new Mock<IVisitorElement<MotionInfo>>();
           
            var converter = new MotionDetectionConverter(visitorMock.Object, parsedDetectionModelMock.Object, new NullLogger<MotionDetectionConverter>());
            var infoConverter = new MotionInfoConverter(visitorMock.Object, parsedInfoModelMock.Object, new NullLogger<MotionInfoConverter>());
            
            JsonSerializerOptions serializeOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
            serializeOptions.Converters.Add(converter);
            serializeOptions.Converters.Add(infoConverter);

            // The Read method of my custom converter class calls the accept 
            // method of a Visitor class, passing the ref struct Utf8JsonReader 
            // and JsonSerializationOptions.
            // I want to use a mock to check that this call is made to the 
            // accept method of the Visitor class.......
            // but this raise a compile error detailed below.

            parsedDetectionModelMock.Setup(x => x.accept(visitorMock, ref reader, serializeOptions));
        }

当我尝试模拟对访问者类的接受方法的调用时:

parsedDetectionModelMock.Setup(x => x.accept(visitorMock, ref reader, serializeOptions));

我收到以下错误:

Cannot use ref local 'reader' inside an anonymous method, lambda expression or query method csharp CS8175

如何使用 Moq 测试接受 ref 结构作为参数的方法?

查看最近的 Moq问题,这似乎是一个已知限制。

这个问题回答了如何测试使用 ref 参数调用的方法。但是,它并没有为 ref 结构提供解决方案。由于 Utf8JsonReader 是一个 ref 结构,它不能用于泛型,例如:

/// fails with cannot use Utf8JsonReader as a type parameter
parsedDetectionModelMock.Setup(x => x.accept(visitorMock.Object, ref It.Ref<Utf8JsonReader>.IsAny, serializeOptions));

开发人员如何测试类似的场景?

是否有任何替代的模拟框架能够管理这个?

标签: c#.net-coremoq

解决方案


推荐阅读