C# 如何模拟非虚拟方法?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11738102/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 19:01:45  来源:igfitidea点击:

How to mock non virtual methods?

c#.netmoq

提问by hbmaam

[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.SendEmailmethod must be virtual to mock it. Is there any way to mock non virtual methods?

EmailService.SendEmail方法必须是虚拟的才能模拟它。有没有办法模拟非虚拟方法?

采纳答案by aqwert

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

Moq 不能模拟类上的非虚拟方法。要么使用其他模拟框架,例如Type mock Isolator,它实际上将 IL 编织到您的程序集中EmailService,要么在其上放置一个接口并模拟它。

回答by Alexander R

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 IEmailServiceto let you mock any of its methods. Of course, you'll have to change the types of variables containing EmailServiceobjects to IEmailServicewhere appropriate.

现在您可以创建接口的IEmailService模拟来模拟它的任何方法。当然,您必须在适当的地方更改包含EmailService对象的变量类型IEmailService

回答by Felice Pollano

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

模拟非虚拟方法涉及使用低级分析器 API。目前我认为唯一可用的选择是:

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

两者都是商业的,即使 JustMock 有一个精简版,模拟非虚拟方法也仅适用于商业版本。正如评论中所指出的,微软的研究中有一些东西,在Pex 和 Moles项目中

回答by Sam

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

正如@aqwert 和@Felice 在使用Typemock Isolator所写的那样,可以(并且非常容易)模拟非虚拟方法而无需添加或更改任何代码,例如:

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

如您所见,我创建的测试与您尝试创建的测试类似。

回答by Rakesh Raut

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();
    }
}

回答by James Reategui

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

使用pose. 允许您替换任何方法,包括静态或非虚拟方法。相当新的项目,但完全开源 MIT 许可证。 https://github.com/tonerdo/pose

回答by Serg046

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

我很久以前就看到了这个问题,并意识到我想创建一些开源的东西来解决这个问题。所以它准备好了 - AutoFake。最现有的事情是它不需要任何疯狂的 CLR Profiler API。它只是一个普通的 .NET 包,仅此而已。Bellow 是您可以使用该库执行的操作的示例:

public class Calendar
{
    public static DateTime Yesterday => DateTime.Now.AddDays(-1);
    internal Task<DateTime> AddSomeMinutesAsync(DateTime date)
        => Task.Run(() => date.AddMinutes(new Random().Next(1, 10)));
}

[Fact]
public void Yesterday_SomeDay_ThePrevDay()
{
    var fake = new Fake<Calendar>();

    fake.Rewrite(f => Calendar.Yesterday)
        .Replace(() => DateTime.Now)
        .Return(new DateTime(2016, 8, day: 8));

    fake.Execute(() => Assert.Equal(new DateTime(2016, 8, 7), Calendar.Yesterday));
}

[Fact]
public async Task AddSomeMinutes_SomeDay_MinutesAdded()
{
    var randomValue = 7;
    var fake = new Fake<Calendar>();

    fake.Rewrite(f => f.AddSomeMinutesAsync(Arg.IsAny<DateTime>()))
        .Replace((Random r) => r.Next(1, 10)) // Arg.Is<int>(i => i == 10) is also possible
        .CheckArguments() // r.Next(1, 11) fails with "Expected - 11, actual - 10"
        .ExpectedCalls(c => c > 0) // c => c > 1 fails with "Actual value - 1"
        .Return(randomValue);

    await fake.ExecuteAsync(async calendar => Assert.Equal(new DateTime(2016, 8, 8, 0, minute: randomValue, 0),
        await calendar.AddSomeMinutesAsync(new DateTime(2016, 8, 8, hour: 0, minute: 0, second: 0))));
}