New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Mocking framework architecture & discussion #4670
Comments
There is one more further consideration we need to put to properly implement the mocking framework. It all lies with the fact that the type equivalence is fundamentally broken. See this report for details. The expectation is that it will not be fixed anytime soon so we must work around it. The tenative idea was to create a To avoid runtime errors due to invalid cast/ type mismatch, etc. we must ensure that there is only one type created for each COM interface. The plan was to use a static cache so that COM interfaces defined outside the VBA project would be always mapped to the same type that was created from a single invocation of the aforementioned methods. However, this will not work for VBA's types. Once PR #4706 is merged, we will be able to create We also have 2nd problem most COM coclasses are in fact composed of several interfaces. To ensure that the type casting succeeds when using the implemented interfaces, we must proactively create all We may also need the changes introduced in the PR #4535 to enable access to entire VBA project and thus enabling mocking of those types. To that end, we would need to be able to do the following:
So in end, we are looking at needing a caching service for Thoughts/suggestions welcome. |
We now have a working proof of concept for mocking a COM type:
At the moment, this only work on any COM types and only the
SetupProperty
is implemented. This works by basically grabbing theITypeInfo
for the given COM type, convert it intoType
(e.g. usingMarshal.GetTypeForITypeInfo
. With aType
, we can then build aMoq.Mock
around it.In theory, it would work for VBA classes, too, since we have the
ITypeInfo
; however there is an issue with theTypeLibConverter
class, which will throw a null reference object deep within mscorlib (specifically somewhere inside this method). For now, I'm attributing it to the fact that VBA's TypeLib/TypeInfo may not be fully compatible, and we need to find a workaround for that.Even so, it should not prevent us from being able to mock the rest of COM types, which would enable us to write unit tests that take those as parameters. For example, a function that take
Excel.Range
can be now directly unit tested using this technique.However, the code required to set up a Moq Mock with a type specified at runtime is quite more complicated. Contrast the code:
Typical Mock setup with compile-time generics:
To the code required to handle a type that's specified at the runtime:
x => x.Name
(Name
is a user-defined parameter that's a string containingName
as the value):Setup(expression).Returns(value)
:We cannot just pass the
expression
directly to theMock.Setup
for following reasons:Setup
is exposed only on theMock<>
generic, not on theMock
class.Returns
, the expression must be ofExpression<Func <T, TResult>>
, and we do not know eitherT
orTResult
at the compile time.ComMock<T>
but that won't help us because of theTResult
not being known until runtime and we'd have to somehow dynamically call the closed generic method.Thus for those reasons, both the lambda expressions and the Mock's setup code must be all built with expression trees, which requires us to know a bit more about the Moq's internals. It should be observed that if there's a breaking API change, the impact is amplified because of several statements to construct an equivalent expression.
OTOH, because it's literally a Moq wrapper, that means we can piggyback the Moq's investment in developing and testing the mocking; our concerns are limited to setting up the mock and translating the user's code into an equivalent expression tree.
Assuming this is deemed viable, I'm considering doing this piecemeal, perhaps starting out with only basic support for simple scenario and enabling the more complex scenarios that Moq can support (e.g. raising events for instance). We could mark this with the
[Experimental]
attribute and ship with only the simpleSetup
scenario enabled. What do you think?NB: for some historical discussion: #1550
The text was updated successfully, but these errors were encountered: