Skip to content

Instantly share code, notes, and snippets.

@vova-lantsov-dev
Last active March 29, 2020 15:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vova-lantsov-dev/318fe6af7489d99d80ba25a155e641de to your computer and use it in GitHub Desktop.
Save vova-lantsov-dev/318fe6af7489d99d80ba25a155e641de to your computer and use it in GitHub Desktop.
[SuppressMessage("ReSharper", "PossibleNullReferenceException")]
[SuppressMessage("ReSharper", "AssignNullToNotNullAttribute")]
private static IServiceProvider FakeServiceProviderWithSectionController
{
get
{
var rendererFactoryMock = new Mock<IRendererFactory>();
var serviceProviderMock = new Mock<IServiceProvider>();
var layoutProviderMock = new Mock<ILayoutProvider>();
var eventBusMock = new Mock<IEventBus>();
var types = typeof(ContentItem).Assembly.GetTypes();
var imageInfoMock = new Mock<Graphics.ImageInfo>();
var graphicsMock = new Mock<Graphics>();
var nativeDocMock = new Mock<INativeDocument>();
var docServiceMock = new Mock<IDocumentService>();
var documentMock = new Mock<IDocument>();
var outlineServiceMock = new Mock<IOutlineService>();
var multilevelListServiceMock = new Mock<IMultilevelListService>();
var occurredEvents = new List<string>();
var snapshots = new List<LayoutSnapshot>();
var listOfMocks = new List<Mock>
{
rendererFactoryMock, serviceProviderMock, layoutProviderMock, eventBusMock, imageInfoMock,
graphicsMock, nativeDocMock, docServiceMock, documentMock, outlineServiceMock,
multilevelListServiceMock
};
// Setup graphics mocks
graphicsMock
.Setup(graphics => graphics.LoadImage(It.IsAny<byte[]>()))
.Returns(() => imageInfoMock.Object);
graphicsMock
.Setup(graphics => graphics.LoadImage(It.IsAny<string>()))
.Returns(() => imageInfoMock.Object);
graphicsMock
.Setup(g => g.GetFontBBox(It.IsAny<Font>()))
.Returns(() => new Box(11.43f, -2.02f, 11.92f, -2.71f));
graphicsMock
.Setup(g => g.MeasureText(It.IsAny<string>(), It.IsAny<Font>(), It.IsAny<float>(), It.IsAny<bool>(),
ref It.Ref<float>.IsAny))
.Returns(20);
nativeDocMock
.Setup(d => d.CreateGraphics(It.IsAny<uint>()))
.Returns(() => graphicsMock.Object);
// Setup CurrentPage property that will be incremented on AddNewPage call
nativeDocMock
.SetupProperty(d => d.CurrentPage);
// Looking for all existing ContentItem-derived types
// f.e. Line, Image, StackFlow
foreach (var contentItemType in types.Where(t => t.IsSubclassOf(typeof(ContentItem))))
{
// Looking for the layouter derived from BaseLayouter<T> where T is contentItemType
// f.e. BaseLayouter<Line>
var layouterType =
types.FirstOrDefault(t =>
t.IsSubclassOf(typeof(BaseLayouter<>).MakeGenericType(contentItemType)));
var layouterTypeToRegister = typeof(ILayouter<>).MakeGenericType(contentItemType);
// Create the Mock<ILayouter<T>> instance
var layouterMockType = typeof(Mock<>).MakeGenericType(layouterTypeToRegister);
dynamic layouterMock = (Mock) Activator.CreateInstance(layouterMockType);
{
// dynamically create the following lambdas
// Expression<Func<ILayouter<T>, LayoutResult>> setupGetNextLayoutExpression = layouter =>
// layouter.GetNextLayout(It.IsAny<object>(), It.IsAny<LayoutingContext>());
// Expression<Action<ILayouter<T>> setupBeginPipelineExpression = layouter =>
// layouter.BeginPipeline(It.IsAny<object>(), It.IsAny<LayoutingContext>(), default(CancellationToken));
// 1. layouter
var layouterParameter = Expression.Parameter(layouterTypeToRegister, "layouter");
// 2-1. layouter.GetNextLayout(It.IsAny<object>(), It.IsAny<LayoutingContext>())
var callToGetNextLayout = Expression.Call(layouterParameter,
typeof(ILayouter).GetMethod("GetNextLayout", BindingFlags.Instance | BindingFlags.Public),
Expression.Call(typeof(It), "IsAny", new[] {typeof(object)}),
Expression.Call(typeof(It), "IsAny", new[] {typeof(LayoutingContext)}));
// 2-2. layouter.BeginPipeline(It.IsAny<object>(), It.IsAny<LayoutingContext>(), default(CancellationToken))
var callToBeginPipeline = Expression.Call(layouterParameter,
typeof(ILayouter).GetMethod("BeginPipeline", BindingFlags.Instance | BindingFlags.Public),
Expression.Call(typeof(It), "IsAny", new[] {typeof(object)}),
Expression.Call(typeof(It), "IsAny", new[] {typeof(LayoutingContext)}),
Expression.Constant(default(CancellationToken), typeof(CancellationToken)));
// 3-1. layouter => layouter.GetNextLayout(It.IsAny<object>(), It.IsAny<LayoutingContext>())
dynamic setupGetNextLayoutExpression = Expression.Lambda(
typeof(Func<,>).MakeGenericType(layouterTypeToRegister, typeof(LayoutResult)),
callToGetNextLayout, layouterParameter);
// 3-2. layouter => layouter.BeginPipeline(
// It.IsAny<object>(), It.IsAny<LayoutingContext>(), default(CancellationToken))
dynamic setupBeginPipelineExpression = Expression.Lambda(
typeof(Action<>).MakeGenericType(layouterTypeToRegister),
callToBeginPipeline, layouterParameter);
var layouterInstance = new Lazy<ILayouter>(() =>
(ILayouter) ActivatorUtilities.CreateInstance(serviceProviderMock.Object, layouterType));
// dynamically add the tracing for calls of GetNextLayout() method
// 1. typeof(IReturns<ILayouter<T>, LayoutResult>)
var returnsType =
typeof(IReturns<,>).MakeGenericType(layouterTypeToRegister, typeof(LayoutResult));
// 2. Call the Returns<object, LayoutingContext>(Func<object, LayoutingContext, LayoutResult> func)
returnsType
.GetMethods()
.First(m => m.IsGenericMethod && m.Name == "Returns" &&
m.GetGenericArguments().Length == 2)
.MakeGenericMethod(typeof(object), typeof(LayoutingContext))
.Invoke((object) layouterMock.Setup(setupGetNextLayoutExpression), new object[]
{
(Func<object, LayoutingContext, LayoutResult>) ((item, context) =>
{
var layouter = layouterInstance.Value;
var result = layouter.GetNextLayout(item, context);
serviceProviderMock.Object
.GetRequiredService<IEventTracingController>()
.AppendMessage($"Got layout result with state: {result.State}");
return result;
})
});
((ICallback) layouterMock.Setup(setupBeginPipelineExpression))
.Callback<object, LayoutingContext, CancellationToken>((item, context, token) =>
{
Task.Run(() =>
{
var layouter = (ILayouter) layouterMock.Object;
foreach (var nextContext in context.InputPipe.Reader.GetConsumingEnumerable(token))
{
try
{
LayoutResult result = layouter.GetNextLayout(item, nextContext);
context.OutputPipe.Writer.Write(result);
}
catch (Exception e)
{
LayoutResult result = new LayoutResult(item as ContentItem);
result.ReportException(e);
context.OutputPipe.Writer.Write(result);
throw;
}
}
});
});
}
// Defer the current Mock<ILayouter<T>> to the list of mocks so it
// will be registered later in the DI together with its object ILayouter<T>
listOfMocks.Add(layouterMock);
var rendererType = typeof(IRenderer<>).MakeGenericType(contentItemType);
// Create the Mock<IRenderer<T>> instance
// f.e. Mock<IRenderer<Line>>
var rendererMockType = typeof(Mock<>).MakeGenericType(rendererType);
dynamic rendererMock = (Mock) Activator.CreateInstance(rendererMockType);
{
// dynamically create the following lambda
// Expression<Action<IRenderer<T>>> setupExpression = renderer =>
// renderer.Render(It.IsAny<LayoutSnapshot>());
// 1. renderer
var rendererParameter = Expression.Parameter(rendererType, "renderer");
// 2. renderer.Render(It.IsAny<LayoutSnapshot>())
var callToRender = Expression.Call(rendererParameter,
typeof(IRenderer).GetMethod("Render", BindingFlags.Instance | BindingFlags.Public),
Expression.Call(typeof(It), "IsAny", new[] {typeof(LayoutSnapshot)}));
// 3. renderer => renderer.Render(It.IsAny<LayoutSnapshot>())
dynamic setupExpression = Expression.Lambda(typeof(Action<>).MakeGenericType(rendererType),
callToRender, rendererParameter);
// dynamically add the tracing for calls of Render() method
((ICallback) rendererMock
.Setup(setupExpression))
.Callback<LayoutSnapshot>(snapshot =>
{
serviceProviderMock.Object
.GetRequiredService<IEventTracingController>()
.AppendSnapshot(snapshot, nativeDocMock.Object);
});
}
// Defer the current Mock<IRenderer<T>> to the list of mocks so it
// will be registered later in the DI together with its object IRenderer<T>
listOfMocks.Add(rendererMock);
}
// Setup the renderer factory which gets the renderers from the service provider
rendererFactoryMock
.Setup(rendererFactory => rendererFactory.GetRenderer(It.IsAny<ContentItem>()))
.Returns<ContentItem>(contentItem =>
(IRenderer) serviceProviderMock.Object.GetService(
typeof(IRenderer<>).MakeGenericType(contentItem.GetType())));
// Setup the layouter factory which gets the layouters from the service provider
layoutProviderMock
.Setup(layoutProvider => layoutProvider.GetLayouter(It.IsAny<Type>()))
.Returns<Type>(itemType =>
(ILayouter) serviceProviderMock.Object.GetService(
typeof(ILayouter<>).MakeGenericType(itemType)));
// Setup document service that will be used for adding the pages
docServiceMock
.Setup(service => service.NativeDocument)
.Returns(() => nativeDocMock.Object);
docServiceMock
.Setup(service => service.AddNewPage(It.IsAny<float>(), It.IsAny<float>()))
.Callback(() =>
{
serviceProviderMock.Object.GetRequiredService<IEventTracingController>()
.AppendMessage($"Page {++nativeDocMock.Object.CurrentPage} created");
})
.Returns(() => nativeDocMock.Object.CurrentPage);
// Setup event bus events
/*eventBusMock
.Setup(bus => bus.Publish(It.IsAny<NewPageCreated>()))
.Callback<NewPageCreated>(newPageCreated =>
OnControllerEventOccurred($"Page {newPageCreated.PageNumber} created"));*/
// Setup controller context and section controller services
serviceProviderMock
.Setup(serviceProvider => serviceProvider.GetService(typeof(ControllerContext)))
.Returns(() =>
ActivatorUtilities.CreateInstance(serviceProviderMock.Object, typeof(ControllerContext)));
serviceProviderMock
.Setup(serviceProvider => serviceProvider.GetService(typeof(SectionController)))
.Returns(() =>
ActivatorUtilities.CreateInstance(serviceProviderMock.Object, typeof(SectionController)));
// For each mock created above...
foreach (var mockInstance in listOfMocks)
{
// Add the instance of current mock to the service provider so we can call
// f.e. serviceProvider.GetService<Mock<IEventBus>>() that returns Mock<IEventBus>
// and then run additional setup actions or verification for this mock instance
// outside of current property
serviceProviderMock
.Setup(serviceProvider => serviceProvider.GetService(mockInstance.GetType()))
.Returns(mockInstance);
// Get the type of generic parameter of current mock
// f.e. for Mock<IEventBus> we need to get the type of IEventBus
var mockGenericParameter = mockInstance.GetType()
.GetGenericArguments()[0];
// Register the service returning its mocked object
// f.e. GetService(typeof(IEventBus)) returns Mock<IEventBus>.Object
// Instead of exact value the mock returns the lambda because we don't want to
// initialize the mock object immediately just here
// (mock object will be initialized later if called by GetService)
serviceProviderMock
.Setup(serviceProvider => serviceProvider.GetService(mockGenericParameter))
.Returns(() => mockInstance.Object)
/*.Callback<Type>(t =>
{
if (typeof(IRenderer).IsAssignableFrom(t))
serviceProviderMock.Object.GetRequiredService<IEventTracingController>()
.AppendMessage(t.ToPrettyName() + " created");
})*/;
}
// Add the tracing for internal events
serviceProviderMock
.Setup(serviceProvider => serviceProvider.GetService(typeof(IEventTracingController)))
.Returns(() => new EventTracingController(occurredEvents, snapshots));
return serviceProviderMock.Object;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment