Reputation: 5097
I really appreciate Moq's Loose
mocking behaviour that returns default values when no expectations are set. It's convenient and saves me code, and it also acts as a safety measure: dependencies won't get unintentionally called during the unit test (as long as they are virtual).
However, I'm confused about how to keep these benefits when the method under test happens to be virtual.
In this case I do want to call the real code for that one method, while still having the rest of the class loosely mocked.
All I have found in my searching is that I could set mock.CallBase = true
to ensure that the method gets called. However, that affects the whole class. I don't want to do that because it puts me in a dilemma about all the other properties and methods in the class that hide call dependencies: if CallBase is true then I have to either
What I think I want is something like:
mock.Setup(m => m.VirtualMethod()).CallBase();
so that when I call mock.Object.VirtualMethod()
, Moq calls into the real implementation...
Q: With Moq, is there any way to test a virtual method, when I mocked the class to stub just a few dependencies? I.e. Without resorting to CallBase=true and having to stub all of the dependencies?
Example code to illustrate
(uses MSTest, InternalsVisibleTo DynamicProxyGenAssembly2)
In the following example, TestNonVirtualMethod
passes, but TestVirtualMethod
fails - returns null.
public class Foo
{
public string NonVirtualMethod() { return GetDependencyA(); }
public virtual string VirtualMethod() { return GetDependencyA();}
internal virtual string GetDependencyA() { return "! Hit REAL Dependency A !"; }
// [... Possibly many other dependencies ...]
internal virtual string GetDependencyN() { return "! Hit REAL Dependency N !"; }
}
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestNonVirtualMethod()
{
var mockFoo = new Mock<Foo>();
mockFoo.Setup(m => m.GetDependencyA()).Returns(expectedResultString);
string result = mockFoo.Object.NonVirtualMethod();
Assert.AreEqual(expectedResultString, result);
}
[TestMethod]
public void TestVirtualMethod() // Fails
{
var mockFoo = new Mock<Foo>();
mockFoo.Setup(m => m.GetDependencyA()).Returns(expectedResultString);
// (I don't want to setup GetDependencyB ... GetDependencyN here)
string result = mockFoo.Object.VirtualMethod();
Assert.AreEqual(expectedResultString, result);
}
string expectedResultString = "Hit mock dependency A - OK";
}
Upvotes: 60
Views: 45937
Reputation: 61952
I believe Lunivore's answer was correct at the time it was written.
In newer versions of Moq (I think since version 4.1 from 2013) it is possible to do what you want with the exact syntax you propose. That is:
mock.Setup(m => m.VirtualMethod()).CallBase();
This sets up the loose mock to call the base implementation of VirtualMethod
instead of just returning default(WhatEver)
, but for this member (VirtualMethod
) only.
As user BornToCode notes in the comments, this will not work if the method has return type void
. When the VirtualMethod
is non-void, the Setup
call gives a Moq.Language.Flow.ISetup<TMock, TResult>
which inherits the CallBase()
method from Moq.Language.Flow.IReturns<TMock, TResult>
. But when the method is void, we get a Moq.Language.Flow.ISetup<TMock>
instead which lacks the desired CallBase()
method.
Update: andrew.rockwell notes below that it works now for void
methods, and apparently that was fixed in version 4.10 (from 2018).
Upvotes: 34
Reputation: 29876
There is a way to call the real method and have a call back when the method is void
, but it's really hacky. You have to make your call back call it explicitly and trick Moq into calling the real one.
For example, given this class
public class MyInt
{
public bool CalledBlah { get; private set; }
public virtual void Blah()
{
this.CalledBlah = true;
}
}
You can write your test this way:
[Test]
public void Test_MyRealBlah()
{
Mock<MyInt> m = new Mock<MyInt>();
m.CallBase = true;
bool calledBlah = false;
m.When(() => !calledBlah)
.Setup(i => i.Blah())
.Callback(() => { calledBlah = true; m.Object.Blah(); })
.Verifiable();
m.Object.Blah();
Assert.IsTrue(m.Object.CalledBlah);
m.VerifyAll();
}
The key aspect is that you track if the fake version has been called, and then you set the mock up to not call the fake if it's already been called.
You can still do something similar if you take args and the value matters:
public class MyInt
{
public List<int> CalledBlahArgs { get; private set; }
public MyInt()
{
this.CalledBlahArgs = new List<int>();
}
public virtual void Blah(int a)
{
this.CalledBlahArgs.Add(a);
}
}
[Test]
public void Test_UpdateQueuedGroups_testmockcallback()
{
Mock<MyInt> m = new Mock<MyInt>();
m.CallBase = true;
List<int> fakeBlahArgs = new List<int>();
m.Setup(i => i.Blah(It.Is<int>(a => !fakeBlahArgs.Contains(a))))
.Callback<int>((a) => { fakeBlahArgs.Add(a); m.Object.Blah(a); })
.Verifiable();
m.Object.Blah(1);
Assert.AreEqual(1, m.Object.CalledBlahArgs.Single());
m.Verify(b => b.Blah(It.Is<int>(id => 1 == id)));
}
Upvotes: -1
Reputation: 17602
Since nobody's answered this question for ages and I think it deserves an answer, I'm going to focus on the highest-level question you asked: how to keep these benefits when the method under test happens to be virtual.
Quick answer: you can't do it with Moq, or at least not directly. But, you can do it.
Let's say that you have two aspects of behaviour, where aspect A is virtual and aspect B is not. That pretty much mirrors what you've got in your class. B can use other methods or not; it's up to you.
At the moment, your class Foo
is doing two things - both A and B. I can tell that they're separate responsibilities just because you want to mock A out and test B on its own.
Instead of trying to mock out the virtual method without mocking out anything else, you can:
Foo
through Foo
's constructorNow you can mock A, and still call the real code for B..N without actually invoking the real A. You can either keep A virtual or access it through an interface and mock that. This is in line with the Single Responsibility Principle, too.
You can cascade constructors with Foo
- make the constructor Foo()
call the constructor Foo(new A())
- so you don't even need a dependency injection framework to do this.
Hope this helps!
Upvotes: 8