Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/261.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Moq您(可以吗?)如何向同一上下文添加两个不同(不兼容)的接口?_C#_Unit Testing_Moq - Fatal编程技术网

C# Moq您(可以吗?)如何向同一上下文添加两个不同(不兼容)的接口?

C# Moq您(可以吗?)如何向同一上下文添加两个不同(不兼容)的接口?,c#,unit-testing,moq,C#,Unit Testing,Moq,我的上下文是对我的数据模型的模拟 我在另一个名为“Email”的类中有一个方法“Send” 我的服务类使用模拟数据模型。 我的服务类“SendEmailForAlarm”中的一个方法访问模拟数据模型中的数据,然后调用Email类中的“Send”方法。 问题:我怎样才能让电子邮件类中的“Send”方法包含在我的模拟中 相关代码: //INTERFACES public interface IEmail { ... void Send(string from, string to, stri

我的上下文是对我的数据模型的模拟 我在另一个名为“Email”的类中有一个方法“Send” 我的服务类使用模拟数据模型。 我的服务类“SendEmailForAlarm”中的一个方法访问模拟数据模型中的数据,然后调用Email类中的“Send”方法。 问题:我怎样才能让电子邮件类中的“Send”方法包含在我的模拟中

相关代码:

//INTERFACES
public interface IEmail
{
  ...
  void Send(string from, string to, string subject, string body, bool isHtml = false);
}
public interface IEntityModel : IDisposable
{
    ...
    DbSet<Alarm> Alarms { get; set; } // Alarms
}

//CLASSES
public class Email : IEmail
{
    ...
    public void Send(string from, string to, string subject, string body, bool isHtml = false)
    {
      ...sends the email
    }
}
public partial class EntityModel : DbContext, IEntityModel
{
    ...
    public virtual DbSet<Alarm> Alarms { get; set; } // Alarms
}
public class ExampleService : ExampleServiceBase
{
    //Constructor
    public ExampleService(IEntityModel model) : base(model) { }

    ...
    public void SendEmailForAlarm(string email, Alarm alarm)
    {
        ...
        new Email().Send(from, to, subject, body, true);
    }
}

//HELPER method
public static Mock<DbSet<T>> GetMockQueryable<T>(Mock<DbSet<T>> mockSet, IQueryable<T> mockedList) where T : BaseEntity
{
    mockSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(mockedList.Expression);
    mockSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(mockedList.ElementType);
    mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(mockedList.GetEnumerator());
    mockSet.Setup(m => m.Include(It.IsAny<string>())).Returns(mockSet.Object);

    // for async operations
    mockSet.As<IDbAsyncEnumerable<T>>()
        .Setup(m => m.GetAsyncEnumerator())
        .Returns(new TestDbAsyncEnumerator<T>(mockedList.GetEnumerator()));

    mockSet.As<IQueryable<T>>()
       .Setup(m => m.Provider)
       .Returns(new TestDbAsyncQueryProvider<T>(mockedList.Provider));

    return mockSet;
}
//接口
公共接口电子邮件
{
...
void Send(string from、string to、string subject、string body、bool isHtml=false);
}
公共接口IEntityModel:IDisposable
{
...
DbSet报警{get;set;}//报警
}
//班级
公共类电子邮件:IEmail
{
...
public void Send(string from、string to、string subject、string body、bool isHtml=false)
{
…发送电子邮件
}
}
公共部分类EntityModel:DbContext,IEntityModel
{
...
公共虚拟数据库集报警{get;set;}//报警
}
公共类ExampleService:ExampleServiceBase
{
//建造师
公共示例服务(IEntityModel模型):基(模型){}
...
公共无效SendEmailForAlarm(字符串电子邮件、警报)
{
...
新建电子邮件()。发送(发件人、收件人、主题、正文、true);
}
}
//辅助方法
公共静态Mock GetMockQueryable(Mock mockSet,IQueryable mockedList),其中T:BaseEntity
{
mockSet.As().Setup(m=>m.Expression).Returns(mockedList.Expression);
mockSet.As().Setup(m=>m.ElementType).Returns(mockedList.ElementType);
mockSet.As().Setup(m=>m.GetEnumerator()).Returns(mockedList.GetEnumerator());
Setup(m=>m.Include(It.IsAny()).Returns(mockSet.Object);
//用于异步操作
mockSet.As()
.Setup(m=>m.GetAsyncEnumerator())
.Returns(新的TestDbAsyncEnumerator(mockedList.GetEnumerator());
mockSet.As()
.Setup(m=>m.Provider)
.Returns(新的TestDbAsyncQueryProvider(mockedList.Provider));
返回模拟集;
}
以下单元测试通过,但不是为了验证电子邮件是否实际发送(或调用) service.SendEmailForAlarm方法查询EntityModel.Alarms数据库集,并从检索到的对象中提取信息。然后调用Email类中的Send方法并返回void

[TestMethod]
public void CurrentTest()
{
    //Data
    var alarms = new List<Alarm> {CreateAlarmObject()}  //create a list of alarm objects

    //Arrange
    var mockContext = new Mock<IEntityModel>();     
    var mockSetAlarm = new Mock<DbSet<Alarm>>();
    var service = new ExampleService(mockContext.Object);

    mockSetAlarm = GetMockQueryable(mockSetAlarm, alarms.AsQueryable());
    mockContext.Setup(a => a.Alarms).Returns(mockSetAlarm.Object);

    //Action
    service.SendEmailForAlarm("test@domain.com", alarms[0]);

    //NOTHING IS ASSERTED, SendEmailForAlarm is void so nothing returned.
}
[TestMethod]
公共测试()
{
//资料
var alarms=new List{CreateAlarmObject()}//创建报警对象列表
//安排
var mockContext=new Mock();
var mockSetAlarm=new Mock();
var service=newexampleservice(mockContext.Object);
mockSetAlarm=GetMockQueryable(mockSetAlarm,alarms.AsQueryable());
mockContext.Setup(a=>a.Alarms).Returns(mockSetAlarm.Object);
//行动
服务。SendEmailForAlarm(“test@domain.com“,报警[0]);
//未声明任何内容,SendEmailForAlarm无效,因此未返回任何内容。
}
我想做的是在Email.Send方法上放置一个拦截器,这样我就可以计算它执行了多少次,或者让mock-verify-validate调用了n次(在本例中是一次)

[TestMethod]
公共无效WhatIWantTest()
{
//资料
var alarms=new List{CreateAlarmObject()}//创建报警对象列表
var=0;
//安排
var mockEmail=new Mock();//新建
var mockContext=new Mock();
var mockSetAlarm=new Mock();
var service=newexampleservice(mockContext.Object);
mockEmail.Setup(u=>
u、 发送(It.IsAny(),It.IsAny(),It.IsAny(),
It.IsAny(),It.IsAny())
.Callback(()=>calls++);//新建在Email.Send方法上添加回拨
mockSetAlarm=GetMockQueryable(mockSetAlarm,alarms.AsQueryable());
mockContext.Setup(a=>a.Alarms).Returns(mockSetAlarm.Object);
//行动
服务。SendEmailForAlarm(“test@domain.com“,报警[0]);
//断言新的
Assert.AreEqual(1,调用);//检查回调计数以验证是否执行了发送
//或
//去掉上面的方法拦截器行,只验证模拟对象。
mockEmail.Verify(m=>m.Send(It.IsAny(),It.IsAny(),
It.IsAny(),It.IsAny(),
It.IsAny()),Times.Once();
}   
当我运行此命令时,断言失败,因为“Calls”=0和Times。one也是零。 我认为我的问题是因为“服务”是使用访问报警数据所需的“mockContext”(IEntityModel)创建的,但Send方法不是mockContext的一部分。如果我模拟“mockEmail”并添加回叫,我就不知道如何将其添加到mockContext中


如何将Mock IEmail和Mock IEntityModel添加到同一上下文中,或者我的做法完全错了吗?

只是澄清一下:您正在创建一个Mock,但从我所看到的情况来看,没有任何东西可以告诉代码使用此Mock进行任何操作。您不需要在任何地方使用该模拟,只需创建它,然后稍后测试它;难怪没有人叫它

我认为您的问题在于您的
ExampleService
正在实例化一个新的
Email()
类:

new Email().Send(from, to, subject, body, true);
而更好的方法是在构造函数中提供
IEmail

    IEmail _email;
    //Constructor
    public ExampleService(IEntityModel model, IEmail email) : base(model) 
    { 
        _email = email;
    }
然后稍后使用此选项:

    public void SendEmailForAlarm(string email, Alarm alarm)
    {
        _email.Send(from, to, subject, body, true);
    }
然后,您可以将您的
Mock
注入到
ExampleService
中,并检查是否适当地调用了它

如果很难/不可能为构造函数注入正确设置DI,那么您可以拥有一个公共属性,它允许您将类中的IEmail替换为另一个用于单元测试的IEmail,但这有点混乱:

public class ExampleService
{
    private IEmail _email = null;
    public IEmail Emailer
    {
        get
        {
            //if this is the first access of the email, then create a new one...
            if (_email == null)
            {
                _email = new Email();
            }
            return _email;
        }
        set
        {
            _email = value;
        }
    }

    ...
    public void SendEmailForAlarm(string email, Alarm alarm)
    {
        //this will either use a new Email() or use a passed-in IEmail
        //if the property was set prior to this call....
        Emailer.Send(from, to, subject, body, true);
    }
}
然后在你的测试中:

    //Arrange
    var mockEmail = new Mock<IEmail>();         //NEW
    var mockContext = new Mock<IEntityModel>();     
    var mockSetAlarm = new Mock<DbSet<Alarm>>();
    var service = new ExampleService(mockContext.Object);
    //we set the Emailer object to be used so it doesn't create one..
    service.Emailer = mockEmail.Object;
//排列
var mockEmail=new Mock()//新的
var mockContext=new Mock();
var mockSetAlarm=new Mock();
var service=newexampleservice(mockContext.Object);
//我们设置要使用的Emailer对象,这样它就不会创建一个。。
service.Emailer=mockEmail.Object;
如果有一个
public    //Arrange
    var mockEmail = new Mock<IEmail>();         //NEW
    var mockContext = new Mock<IEntityModel>();     
    var mockSetAlarm = new Mock<DbSet<Alarm>>();
    var service = new ExampleService(mockContext.Object);
    //we set the Emailer object to be used so it doesn't create one..
    service.Emailer = mockEmail.Object;