Это немного грубо по краям, но это делает работу:
public class JsonMockConverter : JsonConverter {
static readonly Dictionary<object, Func<object>> mockSerializers = new Dictionary<object, Func<object>>();
static readonly HashSet<Type> mockTypes = new HashSet<Type>();
public static void RegisterMock<T>(Mock<T> mock, Func<object> serializer) where T : class {
mockSerializers[mock.Object] = serializer;
mockTypes.Add(mock.Object.GetType());
}
public override bool CanConvert(Type objectType) => mockTypes.Contains(objectType);
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) =>
throw new NotImplementedException();
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
if (!mockSerializers.TryGetValue(value, out var mockSerializer)) {
throw new InvalidOperationException("Attempt to serialize unregistered mock.");
}
serializer.Serialize(writer, mockSerializer());
}
}
Небольшой метод расширения для простоты использования:
internal static class MockExtensions {
public static Mock<T> RegisterForJsonSerialization<T>(this Mock<T> mock) where T : class {
JsonMockConverter.RegisterMock(
mock,
() => typeof(T).GetProperties().ToDictionary(p => p.Name, p => p.GetValue(mock.Object))
);
return mock;
}
}
Настроить следующим образом:
JsonConvert.DefaultSettings = () => new JsonSerializerSettings {
Converters = new[] { new JsonMockConverter() }
};
А теперь работает следующий код:
public interface IAction {
int IntProperty { get; set; }
}
var actionMock = new Mock<IAction>()
.SetupAllProperties()
.RegisterForJsonSerialization();
var action = actionMock.Object;
action.IntProperty = 42;
Console.WriteLine(JsonConvert.SerializeObject(action));
Сделать так, чтобы вам не приходилось регистрировать свои макеты для сериализации, гораздо сложнее - не существует надежного способа выяснить, что объект является имитатором, и если это так, что тип, это должно быть насмешливым, и если оно равно , то как мы должны сериализовать это, используя данные макета. Это могло быть сделано только с некоторыми действительно противными и хрупкими размышлениями о внутренностях Moq, но давайте не будем идти туда. Впрочем, его можно добавить в сам Moq как функцию.
Это может быть дополнительно расширено с помощью пользовательских способов сериализации - здесь я предположил, что у нас все в порядке с простой сериализацией открытых свойств типа mocked. Это немного наивно - он не будет работать правильно, если вы наследуете интерфейсы, например, потому что Type.GetProperties
получает только свойства, объявленные в самом интерфейсе. Исправление, если это необходимо, оставлено читателю в качестве упражнения.
Расширение этого для десериализации возможно в принципе, но немного сложнее. Было бы очень необычно нуждаться в этом в насмешливых целях, а не в конкретном случае.