-
-
Notifications
You must be signed in to change notification settings - Fork 794
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
Capability of mocking delegates (event handlers) #4
Capability of mocking delegates (event handlers) #4
Conversation
Ah, I've almost forgot: I've changed the Castle-Core references in the Moq.csproj, because in the current revision they were hard-bound to 'net35', while all the other subprojects had a Condition-based reference that automatically switches between 'NET35' and 'NET40'. It was important on my workstation, as my projects are in 4.0 and that hardcoded 'net35' collided with a few things. |
Ideally, fixes outside of the main issue should be worked on separately in another branch and sent as individual pull requests (the csproj/castle thing). Otherwise, it's harder to separate what's essential from what's not... Thanks for the fix. We'll review it soon. |
The fix looks very good overall! There's only a minor issue with a few lines of commented-out code that should be deleted. Thanks for looking into this issue! |
I've merged the other pull request that fixes the Castle references (which was unrelated in this pull request). Could you do a Fetch + Rebase and delete those commented lines? With that I'll go ahead and merge ASAP. Thanks! |
sorry, I thought you were just telling me you will cut them out on the fly during the merge. I'll remove them right away |
Would be great if you could do a Fetch & Rebase so that I can auto-merge. Thanks! |
hmm... I'm not very good at using GIT, I've just miraculously managed to recover from accidental merge between four branches (including remote) and I thought I left it in good shape. I probably have to read more on that, because, honestly, I have no idea what do you mean :) I'll try to sort it out in few hours |
I'm sorry, didn't mean to sound like a git pro, I'm very much a newbie Will have to document it sooner than later... /kzu from mobile
|
Contents: In short, this patch adds support for 'new Mock<DLG>' where DLG is derivative of System.Delegate. This should cover all possible event handlers, both concrete like PropertyChangedEventHandler, and generic like EventHandler<EventArgs<string>>, but also all other delegates like System.Action<..> or System.Func<..>. Previously it was not possible, because the delegates are in fact a compiler-generated classes with parameterized constructors, and the constructor's arguments need to meet several conditions. They couldn't be autogenerated in a standard way the Moq does, and the proxy builder crashed if given a request to make a proxy for a delegate. The idea behind the patch is fairly simple: translate the request from mock-a-delegate into mock-a-normal-class. Thus, if Mock<T> detects that the T is a subclass of System.Delegate, it not now doesn't try to mock the delegate, and executes following workaround: 1) dynamically creates an minimalistic interface[1], which declares a single method, which exacly matches the delegate's type 2) it creates a mock object based on that interface, effectively calling 'new Mock< thatinterface >' 3) constructs a delegate of requested type in a classic way, specifing the newly created proxy object the delegate.Target and the interface's single method as delegate.Method This means that the constructed delegate object is a completely normal delegate object and is not a proxy itself. The proxy is hidden in its Target/Method, and the delegate is properly usable in all normal ways: it can be invoked, used as an event handler, merged with other similar delegates, etc. The Mock<DLG> relays all actions to the actual mock object, thus all Setup/Verify operations work as expected. Notes: [1] it is obvious that it is the delegate's type that dictates the exact shape of the generated interface; the dynamic interfaces are cached and reused Example of use: test if a public event was raised // Arrange var fakeHandler = new Mock<EventHandler>(); var sut = new TestedObject(); sut.TheEvent += fakeHandler.Object; // Act // ...do something that should have an side effect of raising the event // Assert fakeHandler.Verify(eh => eh(sut, It.IsAny<EventArgs>()), Times.Once()); Credits: Over 90% of the patch was originally prepared by RoystonS...@gmail.com (-> http://code.google.com/p/moq/issues/detail?id=235#c2) for svn rev 668 (-> http://code.google.com/p/moq/source/detail?r=668). I've updated his original patch to svn's current head (751), and also for the new repo on github. I've also added support for Mock.Get<>(), as it was overlooked in the original patch and rendered usage of 'functional-style' Mock.Of<>() useless if the mocked delegate was meant to .Verify() something.
Ok, I think I've managed to sort that out. Earlier, all patches were 'based' on my 'private moq4 fork' (which also had 'master' branch copied from yours back then), instead of 'moq4/master' of yours main tree. Now, I rebased them to the current Moq4/master, so the patches refer to the 'most current tip' from your repository. I think this is what you meant? All three issues (delegates, callcounts, and the tiny cosmetic patch) seem to compile correctly, and got their hash-ref updated correctly by github. I just love git more and more :D I just cannot imagine doing reorganizations like that in SVN without tons of cross-merges, and I think it be quite hard in HG, too.. phew:) |
This is great @quetzalcoatl ! Yes, indeed these GIT rebases are pretty awesome :). Allows you to keep the main tree clean even with myriad pull requests merged from all over the place. Merged now! |
Capability of mocking delegates (event handlers)
Contents:
In short, this patch adds support for 'new Mock' where DLG is derivative of System.Delegate. This should cover all possible event handlers, both concrete like PropertyChangedEventHandler, and generic like EventHandler<EventArgs>, but also all other delegates like System.Action<..> or System.Func<..>.
Previously it was not possible, because the delegates are in fact a compiler-generated classes with parameterized constructors, and the constructor's arguments need to meet several conditions. They couldn't be autogenerated in a standard way the Moq does, and the proxy builder crashed if given a request to make a proxy for a delegate.
The idea behind the patch is fairly simple: translate the request from mock-a-delegate into mock-a-normal-class.
Thus, if Mock detects that the T is a subclass of System.Delegate, it not now doesn't try to mock the delegate, and executes following workaround:
This means that the constructed delegate object is a completely normal delegate object and is not a proxy itself. The proxy is hidden in its Target/Method, and the delegate is properly usable in all normal ways: it can be invoked, used as an event handler, merged with other similar delegates, etc. The Mock relays all actions to the actual mock object, thus all Setup/Verify operations work as expected.
Notes:
[1] it is obvious that it is the delegate's type that dictates the exact shape of the generated interface; the dynamic interfaces are cached and reused
Example of use:
test if a public event was raised
Credits:
Over 90% of the patch was originally prepared by RoystonS...@gmail.com (-> http://code.google.com/p/moq/issues/detail?id=235#c2) for svn rev 668 (-> http://code.google.com/p/moq/source/detail?r=668).
I've updated his original patch to svn's current head (751), and also for the new repo on github. I've also added support for Mock.Get<>(), as it was overlooked in the original patch and rendered usage of 'functional-style' Mock.Of<>() useless if the mocked delegate was meant to .Verify() something.