42
[TestMethod] public void TestMethod1() { var mock = new Mock<EmailService>(); mock.Setup(x => x.SendEmail()).Returns(true); var cus = new Customer(); var result = cus.AddCustomer(mock.Object); Assert.IsTrue(result); } public class Customer { public bool AddCustomer(EmailService emailService) { emailService.SendEmail(); Debug.WriteLine("new customer added"); return true; } } public class EmailService { public virtual bool SendEmail() { throw new Exception("send email failed cuz bla bla bla"); } } 

The EmailService.SendEmail method must be virtual to mock it. Is there any way to mock non virtual methods?

1

9 Answers 9

28

Moq cannot mock non virtual methods on classes. Either use other mocking frameworks such as Type mock Isolator which actually weaves IL into your assembly or place an interface on EmailService and mock that.

Sign up to request clarification or add additional context in comments.

2 Comments

I know this a very old post, but had some similar issue. Are there any down sides of converting the method into virtual, when the actual application runs?
I recommend asking a separate question in SO.
9

Mocking non virtual methods involves the use of low level profiler API. At the moment I think the only options available are :

both are commercial, even if JustMock have a lite edition, mocking non virtual methods are available just with the commercial version. As pointed in the comments there is something from Microsoft research, in the project Pex and Moles

2 Comments

I think you can add Microsoft Moles/Pex/Fakes to that list. As a bonus, it's free.
Microsoft Fakes is "free" if you have an enterprise edition of visual studio.
8

Updated july 2020: pose Has been abandoned, current recommendation is: https://github.com/riezebosch/Unmockable Thanks to @Customizer for pointing out.

I was also checking out https://github.com/Serg046/AutoFake which seems viable from @Serg046 —— Use pose. Allows you to replace any method including static or non virtual. Fairly new project, but fully open source MIT license. https://github.com/tonerdo/pose

2 Comments

Seems to be dead: github.com/tonerdo/pose/issues/51. The framework github.com/riezebosch/Unmockable is advertised as an alternative in this issue.
Unmockable unfortunately still forces you to wrap/pollute with IUnmockable which can mess up various libraries & frameworks that rely on codegen or runtime type information to operate
6

The alternative to having to use virtual methods for mocking is to use interfaces. This way you can mock out a whole dependency.

public interface IEmailService { bool SendEmail(); // etc... } public class EmailService : IEmailService { //... } 

Now you can create mocks of the interface IEmailService to let you mock any of its methods. Of course, you'll have to change the types of variables containing EmailService objects to IEmailService where appropriate.

1 Comment

I think using this technique is good, and there are certainly advantages to using interfaces. But if you're evaluating between the two options for a non-virtual method to be mockable: 1) make it virtual, 2) use it as part of the interface, what is the advantage of 2 over 1? Even if you pick 2, then you're effectively making the method virtual since it's now part of an interface.
3

I saw this question much time ago and realized that I would want to create something opensourced to solve the issue. So it is ready - AutoFake. The most exciting thing is that it doesn't require any crazy CLR Profiler API. It is just a normal .NET package and that's it. Bellow is the example of what you can do using the library:

public class Calendar { public static DateTime Yesterday => DateTime.Now.AddDays(-1); internal Task<DateTime> AddSomeMinutesAsync(DateTime date) => Task.Run(() => AddSomeMinutes(date)); public static DateTime AddSomeMinutes(DateTime date) => date.AddMinutes(new Random().Next(1, 10)); } [Fact] public void Yesterday_SomeDay_ThePrevDay() { var fake = new Fake<Calendar>(); var sut = fake.Rewrite(() => Calendar.Yesterday); sut.Replace(() => DateTime.Now).Return(new DateTime(2016, 8, day: 8)); Assert.Equal(new DateTime(2016, 8, 7), sut.Execute()); } [Fact] public async Task AddSomeMinutesAsync_SomeDay_MinutesAdded() { var randomValue = 7; var date = new DateTime(2016, 8, 8, hour: 0, minute: 0, second: 0); var fake = new Fake<Calendar>(); var sut = fake.Rewrite(f => f.AddSomeMinutesAsync(date)); sut.Replace((Random r) => r.Next(1, 10)) // Arg.Is<int>(i => i == 10) is also possible // r.Next(1, 11) fails with "Expected - 11, actual - 10" .ExpectedCalls(1) // c => c > 1 fails with "Actual value - 1" .Return(randomValue); Assert.Equal(date.AddMinutes(randomValue), await sut.Execute()); } [Fact] public void AddSomeMinutes_SomeDay_EventsRecorded() { var events = new List<string>(); var fake = new Fake<Calendar>(); var sut = fake.Rewrite(() => Calendar.AddSomeMinutes(new DateTime(2016, 8, 8))); sut.Prepend(() => events.Add("The first line")); sut.Prepend(() => events.Add("The line before AddMinutes(...) call")) .Before((DateTime date) => date.AddMinutes(Arg.IsAny<int>())); sut.Append(() => events.Add("The line after new Random() call")) .After(() => new Random()); sut.Append(() => events.Add("The last line")); sut.Execute(); Assert.Equal(new[] { "The first line", "The line after new Random() call", // indeed, this call is earlier "The line before AddMinutes(...) call", "The last line" }, events); } 

1 Comment

It's a cool idea, but seriously needs more examples for simple scenarios... If I want a fake that fakes a class, and makes 1 particular method return a known value. I don't see how to do that here,
1

As @aqwert and @Felice wrote when using Typemock Isolator it's possible (and pretty easy) to mock Non-virtual methods without adding or changing any code, for example:

[TestMethod,Isolated] public void TestMethod1() { var mock = Isolate.Fake.Instance<EmailService>(); Isolate.WhenCalled(() => mock.SendEmail()).WillReturn(true); var cust = new Customer(); var result = cust.AddCustomer(mock); Assert.IsTrue(result); } 

as you can see the test i've created is similar to the test you tried to create.

1 Comment

It is commercial package
0

The only way to mock non virtual methods is to mock interface used to implement that class with non virtual methods. Below is the example.

public interface IEmployee { DateTime GetDateofJoining(int id); } public class Employee { public DateTime GetDateofJoining(int id) { return DateTime.Now; } } public class Program { static void Main(string[] args) { var employee = new Mock<IEmployee>(); employee.Setup(x => x.GetDateofJoining(It.IsAny<int>())).Returns((int x) => DateTime.Now); Console.WriteLine(employee.Object.GetDateofJoining(1)); Console.ReadLine(); } } 

Comments

0

How about MockMe? It doesn't support mocking static members (yet), but it does support mocking concrete types without the artificial activity of introducing an interface or making members virtual (furthermore without the need of adding any InternalsVisibleTo):

static void Main(string[] args) { var fakeDriver = Mock.Me(default(BlueToothDriver)); fakeDriver.Setup.ReadString().Returns("world!"); var device = new BlueToothDevice(fakeDriver.MockedObject); device.Hello(); } internal class BlueToothDevice(BlueToothDriver driver) { public void Hello() => Console.WriteLine("Hello {0}", driver.ReadString()); } // e.g. external library internal class BlueToothDriver { public string ReadString() => "doomsday!"; } 

Comments

-1

As a workaround you can use not the method itself but create virtual wrapper method instead

public class EmailService { protected virtual void SendEmailReal(){ throw new Exception("send email failed cuz bla bla bla"); } public void bool SendEmail() { return SendEmailReal(); } } 

And then override it in the test class:

abstract class TestEmailService: EmailService{ public abstract override bool SendEmailReal(); } 

The test methods itself:

[TestMethod] public void TestMethod1() { var mock = new Mock<TestEmailService>(); mock.Setup(x => x.SendEmailReal()).Returns(true); var cus = new Customer(); var result = cus.AddCustomer(mock.Object); Assert.IsTrue(result); } 

1 Comment

this answer did have 2 upwotes but now it has -1, similar things I see with other answers(not mine) over staskowerflow, staskowerflow is accupied by some upset bots?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.