gpt4 book ai didi

entity-framework - 如何在 N 层架构中模拟 Entity Framework

转载 作者:行者123 更新时间:2023-12-04 21:09:40 27 4
gpt4 key购买 nike

我有一个带有 Entity Framework (代码优先方法)的 N 层应用程序。现在我想自动化一些测试。我正在使用 Moq 框架 。我在编写测试时发现了一些问题。也许我的架构是错误的?错了,我的意思是我编写的组件没有很好地隔离,因此它们不可测试。我不太喜欢这个……或者,我根本无法正确使用 moq 框架。

我让你看看我的架构:

enter image description here

在每个级别,我都会在类的构造函数中注入(inject)我的 context

门面:

public class PublicAreaFacade : IPublicAreaFacade, IDisposable
{
private UnitOfWork _unitOfWork;

public PublicAreaFacade(IDataContext context)
{
_unitOfWork = new UnitOfWork(context);
}
}

BLL:
public abstract class BaseManager
{
protected IDataContext Context;

public BaseManager(IDataContext context)
{
this.Context = context;
}
}

存储库:
public class Repository<TEntity>
where TEntity : class
{
internal PublicAreaContext _context;
internal DbSet<TEntity> _dbSet;

public Repository(IDataContext context)
{
this._context = context as PublicAreaContext;
}
}
IDataContext 是我的 DbContext 实现的接口(interface):
public partial class PublicAreaContext : DbContext, IDataContext

现在,我如何模拟 EF 以及如何编写测试:
[TestInitialize]
public void Init()
{
this._mockContext = ContextHelper.CreateCompleteContext();
}
ContextHelper.CreateCompleteContext() 在哪里:
public static PublicAreaContext CreateCompleteContext()
{
//Here I mock my context
var mockContext = new Mock<PublicAreaContext>();

//Here I mock my entities
List<Customer> customers = new List<Customer>()
{
new Customer() { Code = "123455" }, //Customer with no invoice
new Customer() { Code = "123456" }
};

var mockSetCustomer = ContextHelper.SetList(customers);
mockContext.Setup(m => m.Set<Customer>()).Returns(mockSetCustomer);

...

return mockContext.Object;
}

在这里我如何编写我的测试:
[TestMethod]
public void Success()
{
#region Arrange
PrepareEasyPayPaymentRequest request = new PrepareEasyPayPaymentRequest();
request.CodiceEasyPay = "128855248542874445877";
request.Servizio = "MyService";
#endregion

#region Act
PublicAreaFacade facade = new PublicAreaFacade(this._mockContext);
PrepareEasyPayPaymentResponse response = facade.PrepareEasyPayPayment(request);
#endregion

#region Assert
Assert.IsTrue(response.Result == it.MC.WebApi.Models.ResponseDTO.ResponseResult.Success);
#endregion
}

这里似乎一切正常!!!看起来我的架构是正确的。但是如果我想插入/更新一个实体呢?没有任何工作了!我解释为什么:

如您所见,我将一个 *Request 对象(它是 DTO)传递给外观,然后在我的 TOA 中,我从 DTO 的属性生成我的实体:
private PaymentAttemptTrace CreatePaymentAttemptTraceEntity(string customerCode, int idInvoice, DateTime paymentDate)
{
PaymentAttemptTrace trace = new PaymentAttemptTrace();
trace.customerCode = customerCode;
trace.InvoiceId = idInvoice;
trace.PaymentDate = paymentDate;

return trace;
}
PaymentAttemptTrace 是我将插入到 Entity Framework 的实体。它没有被模拟,我无法注入(inject)它。因此,即使我通过了我的模拟上下文(IDataContext),当我尝试插入一个未模拟的实体时,我的测试也会失败!

这里有人怀疑我有一个错误的架构!

那么,怎么了?我使用最小起订量的架构或方式?

谢谢你的帮助

更新

这里我如何测试我的代码..例如,我想测试支付的痕迹..

这里是测试:
[TestMethod]
public void NoPaymentDate()
{
TracePaymentAttemptRequest request = new TracePaymentAttemptRequest();
request.AliasTerminale = "MyTerminal";
//...
//I create my request object

//You can see how I create _mockContext above
PublicAreaFacade facade = new PublicAreaFacade(this._mockContext);
TracePaymentAttemptResponse response = facade.TracePaymentAttempt(request);

//My asserts
}

这里的门面:
public TracePaymentAttemptResponse TracePaymentAttempt(TracePaymentAttemptRequest request)
{
TracePaymentAttemptResponse response = new TracePaymentAttemptResponse();

try
{
...

_unitOfWork.PaymentsManager.SavePaymentAttemptResult(
easyPay.CustomerCode,
request.CodiceTransazione,
request.EsitoPagamento + " - " + request.DescrizioneEsitoPagamento,
request.Email,
request.AliasTerminale,
request.NumeroContratto,
easyPay.IdInvoice,
request.TotalePagamento,
paymentDate);

_unitOfWork.Commit();

response.Result = ResponseResult.Success;
}
catch (Exception ex)
{
response.Result = ResponseResult.Fail;
response.ResultMessage = ex.Message;
}

return response;
}

这是我开发 PaymentsManager 的方式:
public PaymentAttemptTrace SavePaymentAttemptResult(string customerCode, string transactionCode, ...)
{
//here the problem... PaymentAttemptTrace is the entity of entity framework.. Here i do the NEW of the object.. It should be injected, but I think it would be a wrong solution
PaymentAttemptTrace trace = new PaymentAttemptTrace();
trace.customerCode = customerCode;
trace.InvoiceId = idInvoice;
trace.PaymentDate = paymentDate;
trace.Result = result;
trace.Email = email;
trace.Terminal = terminal;
trace.EasypayCode = transactionCode;
trace.Amount = amount;
trace.creditCardId = idCreditCard;
trace.PaymentMethod = paymentMethod;

Repository<PaymentAttemptTrace> repository = new Repository<PaymentAttemptTrace>(base.Context);
repository.Insert(trace);

return trace;
}

最后我是如何编写存储库的:
public class Repository<TEntity>
where TEntity : class
{
internal PublicAreaContext _context;
internal DbSet<TEntity> _dbSet;

public Repository(IDataContext context)
{
//the context is mocked.. Its type is {Castle.Proxies.PublicAreaContextProxy}
this._context = context as PublicAreaContext;
//the entity is not mocked. Its type is {PaymentAttemptTrace} but should be {Castle.Proxies.PaymentAttemptTraceProxy}... so _dbSet result NULL
this._dbSet = this._context.Set<TEntity>();
}

public virtual void Insert(TEntity entity)
{
//_dbSet is NULL so "Object reference not set to an instance of an object" exception is raised
this._dbSet.Add(entity);
}
}

最佳答案

您的架构看起来不错,但实现存在缺陷。它正在泄漏抽象。

在您的图表中,立面 层仅取决于 BLL 但是当您查看 PublicAreaFacade的构造函数,您会看到实际上它直接依赖于 中的接口(interface)。存储库层:

public PublicAreaFacade(IDataContext context)
{
_unitOfWork = new UnitOfWork(context);
}

这不应该。它应该只将其直接依赖作为输入—— PaymentsManager或者——甚至更好——它的一个界面:
public PublicAreaFacade(IPaymentsManager paymentsManager)
{
...
}

结果是您的代码变得更加可测试。现在,当您查看测试时,您会发现必须模拟系统的最内层(即 IDataContext 甚至它的实体访问器 Set<TEntity>),尽管您正在测试系统的最外层之一( PublicAreaFacade 类)。

这就是 TracePaymentAttempt 的单元测试方式。方法看起来像 PublicAreaFacade只依赖 IPaymentsManager :
[TestMethod]
public void CallsPaymentManagerWithRequestDataWhenTracingPaymentAttempts()
{
// Arrange
var pm = new Mock<IPaymentsManager>();
var pa = new PulicAreaFacade(pm.Object);
var payment = new TracePaymentAttemptRequest
{
...
}

// Act
pa.TracePaymentAttempt(payment);

// Assert that we call the correct method of the PaymentsManager with the data from
// the request.
pm.Verify(pm => pm.SavePaymentAttemptResult(
It.IsAny<string>(),
payment.CodiceTransazione,
payment.EsitoPagamento + " - " + payment.DescrizioneEsitoPagamento,
payment.Email,
payment.AliasTerminale,
payment.NumeroContratto,
It.IsAny<int>(),
payment.TotalePagamento,
It.IsAny<DateTime>()))
}

关于entity-framework - 如何在 N 层架构中模拟 Entity Framework ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37272345/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com