Unit testing 单元测试OData V4与XUnit和MOQ一起运行

Unit testing 单元测试OData V4与XUnit和MOQ一起运行,unit-testing,xunit,asp.net-web-api-odata,Unit Testing,Xunit,Asp.net Web Api Odata,我的目标是在ODataV4控制器中对PUT操作进行单元测试 我使用实体框架6上下文的MOQ和NBuilder来构建测试数据 我能够成功地测试Get和Get(Id),但在从PUT操作检索HTTPActionResult时无法运行断言 我可以看到HTTPActionResult在调试模式下返回一个带有实体属性的UpdateDataResult对象,但我看不到在设计时使用它的方法 有人知道如何从异步PUT操作响应中提取返回的实体吗 代码如下: using Models; using WebApp.DA

我的目标是在ODataV4控制器中对PUT操作进行单元测试

我使用实体框架6上下文的MOQ和NBuilder来构建测试数据

我能够成功地测试Get和Get(Id),但在从PUT操作检索HTTPActionResult时无法运行断言

我可以看到HTTPActionResult在调试模式下返回一个带有实体属性的UpdateDataResult对象,但我看不到在设计时使用它的方法

有人知道如何从异步PUT操作响应中提取返回的实体吗

代码如下:

using Models;
using WebApp.DAL;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.OData;

namespace WebApp.Controllers.Api
{
    public class OrgsController : ODataController
    {
        private IWebAppDbContext db = new WebAppDbContext();

        public OrgsController()
        {
        }

        public OrgsController(IWebAppDbContext Context)
        {
           db = Context;
        }

        public async Task<IHttpActionResult> Put([FromODataUri] long Key, Org Entity)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
            if (Key != Entity.Id)
            {
                return BadRequest();
            }

            db.MarkAsModified(Entity);
            try
            {
                await db.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!EntityExists(Key))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }
            return Updated(Entity);
        }
        //...other actions omitted
    }
}
使用模型;
使用WebApp.DAL;
使用System.Data.Entity.Infrastructure;
使用System.Linq;
Net系统;
使用System.Threading.Tasks;
使用System.Web.Http;
使用System.Web.OData;
命名空间WebApp.Controllers.Api
{
公共类组织控制器:ODataController
{
私有IWebAppDbContext db=新WebAppDbContext();
公共组织控制器()
{
}
公共OrgsController(IWebAppDbContext上下文)
{
db=上下文;
}
公共异步任务Put([FromODataUri]长键,组织实体)
{
如果(!ModelState.IsValid)
{
返回请求(ModelState);
}
if(Key!=Entity.Id)
{
返回请求();
}
db.修改的标记(实体);
尝试
{
等待db.saveChangesSync();
}
catch(DbUpdateConcurrencyException)
{
如果(!EntityExists(Key))
{
返回NotFound();
}
其他的
{
投掷;
}
}
更新申报表(实体);
}
//…省略了其他操作
}
}
这是我的单元测试代码

[Theory, InlineData(5)]
public async Task Api_Put_Updates_Properties(long Id)
{            
    //arrange
    var mockedDbContext = MocksFactory.GetMockContext<WebAppDbContext>();
    mockedDbContext.Object.Orgs.AddRange(MocksFactory.GetMockData<Org>(10));                
    OrgsController _sut = new OrgsController(mockedDbContext.Object);
    Org beforeEntity = new Org
    {
        Id = Id,
        Name = "Put Org",
        TaxCountryCode = "PutUs",
        TaxNumber = "PutUs01"
    };

    //act
    IHttpActionResult actionResult = await _sut.Put(Id, beforeEntity); 

    //assert   
    Assert.NotNull(actionResult);
    //Assert.NotNull(actionResult.Entity);
    //Assert.Equal(Id, actionResult.Entity.Id);
    //Assert.Equal("Put Org", actionResult.Entity.Name);
}
[理论,在线数据(5)]
公共异步任务Api\u Put\u更新\u属性(长Id)
{            
//安排
var mockedDbContext=MocksFactory.GetMockContext();
mockedbContext.Object.Orgs.AddRange(MocksFactory.GetMockData(10));
OrgsController _sut=新的OrgsController(mockedBContext.Object);
Org beforeEntity=新组织
{
Id=Id,
Name=“放置组织”,
TaxCountryCode=“PutUs”,
TaxNumber=“PutUs01”
};
//表演
IHttpActionResult actionResult=Wait _sut.Put(Id,beforeEntity);
//断言
Assert.NotNull(actionResult);
//Assert.NotNull(actionResult.Entity);
//Assert.Equal(Id,actionResult.Entity.Id);
//Assert.Equal(“Put Org”,actionResult.Entity.Name);
}

谢谢@Old Fox的建议。以下是我最终得到的结果: *注意:为了便于解释,我加入了在线模拟设置。我使用这种方法在生产代码中创建工厂:

这篇文章还帮助我整理了最低起订量设置:

此外,我在注释中引用的链接显示了如何正确设置GetEnumerator方法。MSDN页面上的说明不正确。 差别很微妙,但意义重大(这一发现花费了我一周的时间)。确保正确设置该片段,否则您的上下文将有一个空的dbset(本例中为Orgs)

[理论,InlineData(1),InlineData(3),InlineData(5)]
公共异步任务Api_Put_Valid_Entity_调用_ContextMethods_并返回_UpdatedODataResult(长键)
{
//安排
var数据=新列表
{
新组织{Id=1,Name=“Name1”,TaxCountryCode=“T1”,TaxNumber=“TaxNumber1”,CreatedBy=1,CreatedOn=DateTime。现在,ModifiedBy=1,ModifiedOn=DateTime。现在,Accounts=null,ChargeRequestsFromMe=null,ChargeRequestsToMe=null,PaymentRequestsFromMe=null,PaymentRequestsToMe=null,Submissions=null},
新组织{Id=2,Name=“Name2”,TaxCountryCode=“T2”,TaxNumber=“TaxNumber2”,CreatedBy=1,CreatedOn=DateTime。现在,ModifiedBy=1,modifieddon=DateTime。现在,Accounts=null,ChargeRequestsFromMe=null,ChargeRequestsToMe=null,PaymentRequestsFromMe=null,PaymentRequestsToMe=null,Submissions=null},
新组织{Id=3,Name=“Name3”,TaxCountryCode=“T3”,TaxNumber=“TaxNumber3”,CreatedBy=1,CreatedOn=DateTime。现在,ModifiedBy=1,ModifiedOn=DateTime。现在,Accounts=null,ChargeRequestsFromMe=null,ChargeRequestsToMe=null,PaymentRequestsFromMe=null,PaymentRequestsToMe=null,Submissions=null},
新组织{Id=4,Name=“Name4”,TaxCountryCode=“T4”,TaxNumber=“TaxNumber4”,CreatedBy=1,CreatedOn=DateTime。现在,ModifiedBy=1,ModifiedOn=DateTime。现在,Accounts=null,ChargeRequestsFromMe=null,ChargeRequestsToMe=null,PaymentRequestsFromMe=null,PaymentRequestsToMe=null,Submissions=null},
新组织{Id=5,Name=“Name5”,TaxCountryCode=“T5”,TaxNumber=“TaxNumber5”,CreatedBy=1,CreatedOn=DateTime。现在,ModifiedBy=1,ModifiedOn=DateTime。现在,Accounts=null,ChargeRequestsFromMe=null,ChargeRequestsToMe=null,PaymentRequestsFromMe=null,PaymentRequestsToMe=null,Submissions=null}
}.AsQueryable();
var mockSet=new Mock();
mockSet.As().Setup(m=>m.Provider).返回(data.Provider);
mockSet.As().Setup(m=>m.Expression).Returns(data.Expression);
mockSet.As().Setup(m=>m.ElementType).Returns(data.ElementType);
mockSet.As().Setup(m=>m.GetEnumerator())。返回(()=>data.GetEnumerator());
Setup(m=>m.Find(It.IsAny())。返回(Id=>data.FirstOrDefault(d=>d.Id==(int)Id[0]);
var mockContext=new Mock();
Setup(m=>m.Orgs).Returns(mockSet.Object);
mockContext.Setup(m=>m.savechangessync()).Returns(()=>Task.Run(()=>{return 1;})).Verifiable();
mockContext.Setup(m=>m.MarkAsModified(It.IsAny()).Verifiable();
组织实体=新组织
{
Id=键,
Name=“放置组织”
[Theory, InlineData(1), InlineData(3), InlineData(5)]
public async Task Api_Put_Valid_Entity_Calls_ContextMethods_And_Returns_UpdatedODataResult(long Key)
{
    //arrange
    var data = new List<Org>
    {
        new Org { Id = 1, Name = "Name1", TaxCountryCode = "T1", TaxNumber = "TaxNumber1", CreatedBy = 1, CreatedOn = DateTime.Now, ModifiedBy = 1, ModifiedOn = DateTime.Now, Accounts = null, ChargeRequestsFromMe = null, ChargeRequestsToMe = null, PaymentRequestsFromMe = null, PaymentRequestsToMe = null, Submissions = null },
        new Org { Id = 2, Name = "Name2", TaxCountryCode = "T2", TaxNumber = "TaxNumber2", CreatedBy = 1, CreatedOn = DateTime.Now, ModifiedBy = 1, ModifiedOn = DateTime.Now, Accounts = null, ChargeRequestsFromMe = null, ChargeRequestsToMe = null, PaymentRequestsFromMe = null, PaymentRequestsToMe = null, Submissions = null },
        new Org { Id = 3, Name = "Name3", TaxCountryCode = "T3", TaxNumber = "TaxNumber3", CreatedBy = 1, CreatedOn = DateTime.Now, ModifiedBy = 1, ModifiedOn = DateTime.Now, Accounts = null, ChargeRequestsFromMe = null, ChargeRequestsToMe = null, PaymentRequestsFromMe = null, PaymentRequestsToMe = null, Submissions = null },
        new Org { Id = 4, Name = "Name4", TaxCountryCode = "T4", TaxNumber = "TaxNumber4", CreatedBy = 1, CreatedOn = DateTime.Now, ModifiedBy = 1, ModifiedOn = DateTime.Now, Accounts = null, ChargeRequestsFromMe = null, ChargeRequestsToMe = null, PaymentRequestsFromMe = null, PaymentRequestsToMe = null, Submissions = null },
        new Org { Id = 5, Name = "Name5", TaxCountryCode = "T5", TaxNumber = "TaxNumber5", CreatedBy = 1, CreatedOn = DateTime.Now, ModifiedBy = 1, ModifiedOn = DateTime.Now, Accounts = null, ChargeRequestsFromMe = null, ChargeRequestsToMe = null, PaymentRequestsFromMe = null, PaymentRequestsToMe = null, Submissions = null }
    }.AsQueryable();

    var mockSet = new Mock<DbSet<Org>>();
    mockSet.As<IQueryable<Org>>().Setup(m => m.Provider).Returns(data.Provider);
    mockSet.As<IQueryable<Org>>().Setup(m => m.Expression).Returns(data.Expression);
    mockSet.As<IQueryable<Org>>().Setup(m => m.ElementType).Returns(data.ElementType);
    mockSet.As<IQueryable<Org>>().Setup(m => m.GetEnumerator()).Returns(() => data.GetEnumerator());
    mockSet.Setup(m => m.Find(It.IsAny<object[]>())).Returns<object[]>(ids => data.FirstOrDefault(d => d.Id == (int)ids[0]));            

    var mockContext = new Mock<MyDbContext>();
    mockContext.Setup(m => m.Orgs).Returns(mockSet.Object);
    mockContext.Setup(m => m.SaveChangesAsync()).Returns(() => Task.Run(() => { return 1; })).Verifiable();
    mockContext.Setup(m => m.MarkAsModified(It.IsAny<Org>())).Verifiable();

    Org entity = new Org
    {
        Id = Key,
        Name = "Put Org",
        TaxCountryCode = "Put" + Key.ToString(),
        TaxNumber = "Put" + Key.ToString()
    };

    var sut = new OrgsController(mockContext.Object);

    //act
    var actionResult = await sut.Put(Key, entity) as UpdatedODataResult<Org>;

    //assert
    mockContext.Verify(m => m.SaveChangesAsync(), Times.Once());
    mockContext.Verify(m => m.MarkAsModified(entity), Times.Once());
    Assert.IsType<UpdatedODataResult<Org>>(actionResult);
    Assert.Equal(entity.Name, actionResult.Entity.Name);
    Assert.Equal(entity.TaxCountryCode, actionResult.Entity.TaxCountryCode);
    Assert.Equal(entity.TaxNumber, actionResult.Entity.TaxNumber);
}