Skip to content
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

Merged
merged 2 commits into from
Jun 25, 2012

Conversation

quetzalcoatl
Copy link
Contributor

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:

  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 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.

@quetzalcoatl
Copy link
Contributor Author

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.

@kzu
Copy link
Contributor

kzu commented Mar 23, 2012

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.

@kzu
Copy link
Contributor

kzu commented May 31, 2012

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!

@kzu
Copy link
Contributor

kzu commented Jun 15, 2012

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!

@quetzalcoatl
Copy link
Contributor Author

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

@kzu
Copy link
Contributor

kzu commented Jun 21, 2012

Would be great if you could do a Fetch & Rebase so that I can auto-merge.

Thanks!

@quetzalcoatl
Copy link
Contributor Author

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

@kzu
Copy link
Contributor

kzu commented Jun 22, 2012

I'm sorry, didn't mean to sound like a git pro, I'm very much a newbie
myself. I just know one possible workflow that works great for pull
requests that I used in another project :).

Will have to document it sooner than later...

/kzu from mobile
On Jun 21, 2012 6:15 PM, "quetzalcoatl" <
reply@reply.github.com>
wrote:

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


Reply to this email directly or view it on GitHub:
#4 (comment)

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.
@quetzalcoatl
Copy link
Contributor Author

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:)

@kzu
Copy link
Contributor

kzu commented Jun 25, 2012

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!

kzu added a commit that referenced this pull request Jun 25, 2012
Capability of mocking delegates (event handlers)
@kzu kzu merged commit e614ae5 into devlooped:master Jun 25, 2012
david-kalbermatten added a commit to david-kalbermatten/moq4 that referenced this pull request Jun 9, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants