-
-
Notifications
You must be signed in to change notification settings - Fork 812
When mocking a class, why are non-virtual methods hidden in the generated proxy? #212
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
Comments
Looks like an implementation detail that changed in the Castle.DynamicProxy On Sat, Oct 31, 2015, 6:28 AM Cristian Diaconescu [email protected]
|
Still seeing this in Moq 4.5.12. Due to the #129 fix, it is possible to Mock<Stream> mock = new Mock<Stream>(MockBehavior.Strict);
mock.As<IDisposable>().Setup(s => s.Dispose());
mock.Protected().Setup("Dispose", true).Callback(() => innerStreamDisposed = true);
// Do something, perhaps w/ a wrapper, that calls `mock.Object.Dispose()`. But approaches like the above don't work at all if the public abstract class ExceptionHandler : IExceptionHandler
{
Task IExceptionHandler.HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
{
// ...
return HandleAsync(context, cancellationToken);
}
public virtual Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
{
// ...
return TaskHelpers.Completed();
}
}
Mock<ExceptionHandler> mock = new Mock<ExceptionHandler>(MockBehavior.Strict);
mock
.As<IExceptionHandler>()
.Setup(h => h.HandleAsync(It.IsAny<ExceptionHandlerContext>(), It.IsAny<CancellationToken>()));
|
Hit Comment too soon. Added another sentence or so. |
General question: Why is it correct for |
- work around devlooped/moq#129 and devlooped/moq#212; Moq creates proxies for all `interface` methods - e.g. set up `IDisposable.Dispose()` calls - related bug devlooped/moq#212 means we can't use `MockBehavior.Strict` in some cases - especially when method returns a value and therefore can't be set up to call base - `CallBase = true` is often required in these cases - work around devlooped/moq#149; `DefaultValue.Mock` restrictions - where necessary, explicitly set up members instead - work around odd failures in `JsonResultTest` using `SetupSet()` on deep properties - use `MockBehavior.Strict` in `NullContentIsNotOutput()` to confirm `Write()` is not called - handle less-predictable proxy type names
- #11 (3 of 4) - work around devlooped/moq#129 and devlooped/moq#212; Moq creates proxies for all `interface` methods - e.g. set up `IDisposable.Dispose()` calls - related bug devlooped/moq#212 means we can't use `MockBehavior.Strict` in some cases - especially when method returns a value and therefore can't be set up to call base - `CallBase = true` is often required in these cases - work around devlooped/moq#149; `DefaultValue.Mock` restrictions - where necessary, explicitly set members up instead - work around odd failures in `JsonResultTest` using `SetupSet()` on deep properties - use `MockBehavior.Strict` in `NullContentIsNotOutput()` to confirm `Write()` is not called - handle less-predictable proxy type names
@diaconesq: I couldn't get your test to work. It either always failed or always succeeded with pretty much any version of Moq. So I changed it to the following. I think it is equivalent to what you originally wanted to demonstrate: public interface ILog
{
void Log(string s);
}
public class MockableLog : ILog
{
public void Log(string s) => WasLogged(s);
public virtual void WasLogged(string message) { }
}
public class UsesLog
{
private readonly ILog log;
public UsesLog(ILog log)
{
this.log = log;
}
public void LogSomething() => log.Log("test 1");
}
[Fact]
public void Test()
{
var mock = new Mock<MockableLog>();
var sut = new UsesLog(mock.Object);
sut.LogSomething();
mock.Verify(log => log.WasLogged(It.IsAny<string>()));
} This test breaks when upgrading from 4.2.1402.2112 (where it passes) to 4.2.1408.619 (where it fails). This has been fixed by #381, so you should be fine once you upgrade to Moq 4.7.63. @dougbu: If you're still interested in an answer to your question above, could you please ask your question as a separate issue so we can track it independently of the originally reported issue? Thanks! :) |
I have noticed a change between Moq 4.1.1 and 4.2.1.
In Moq 4.1.1, when creating a
new Mock<ActualClass>()
, the generated proxy type would not do anything special for non-virtual methods.In Moq 4.2.1, the (public) non-virtual methods are hidden by new implementations (with the
new
keyword) in the generated proxy.Why is that?
I still can't call
.Setup(...)
or.Verify(...)
on non-virtual methods. (throws a 'NotSupported' exception, like it always did. ) It looks as if Moq is preparing things in order to be able to intercept those methods as well.I'm asking because I think this violates the principle of least astonishment.
(I would have expected that calling the non-virtual method on the mock would call the actual implementation in the original class, regardless of specifying CallBase = true or not)
I have attached below a unit test that was broken by upgrading from Moq 4.1.1 to 4.2.1.
The text was updated successfully, but these errors were encountered: