Ich habe eine N-Layer-Anwendung mit Entity Framework (Code-First-Ansatz). Jetzt möchte ich einige Tests automatisieren. Ich verwende Moq Rahmen. Ich finde ein Problem beim Schreiben der Tests. Vielleicht ist meine Architektur falsch? Mit falsch, ich meine, dass ich Komponenten geschrieben habe, die nicht gut isoliert sind und daher nicht testbar sind. Ich mag das nicht wirklich ... Oder vielleicht kann ich moq Framework einfach nicht richtig benutzen.Wie man Entity Framework in einer N-Layer-Architektur vortäuscht
Ich lasse Sie meine Architektur sehen:
Auf jeder Ebene ich meine context
im Konstruktor der Klasse zu injizieren.
Die Fassade:
public class PublicAreaFacade : IPublicAreaFacade, IDisposable
{
private UnitOfWork _unitOfWork;
public PublicAreaFacade(IDataContext context)
{
_unitOfWork = new UnitOfWork(context);
}
}
Der BLL:
public abstract class BaseManager
{
protected IDataContext Context;
public BaseManager(IDataContext context)
{
this.Context = context;
}
}
Das Repository:
public class Repository<TEntity>
where TEntity : class
{
internal PublicAreaContext _context;
internal DbSet<TEntity> _dbSet;
public Repository(IDataContext context)
{
this._context = context as PublicAreaContext;
}
}
IDataContext
ist eine Schnittstelle, die von meinem DbContext implementiert:
public partial class PublicAreaContext : DbContext, IDataContext
Nun, wie ich verspotten EF
und wie schreibe ich die Tests:
[TestInitialize]
public void Init()
{
this._mockContext = ContextHelper.CreateCompleteContext();
}
Wo ContextHelper.CreateCompleteContext()
ist:
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;
}
Und hier, wie ich schreibe meinen Test:
[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
}
Hier scheint es funktioniert alles richtig !!! Und es sieht so aus, als wäre meine Architektur korrekt. Was aber, wenn ich eine Entity einfügen/aktualisieren möchte? Nichts funktioniert mehr! Ich erklären, warum:
Wie Sie gebe ich ein *Request
Objekt sehen kann (es ist das DTO) an die Fassade, dann in meinem TOA ich mein Unternehmen aus der propertiess des DTO erzeugen:
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
ist die Entität Ich werde in Entity Framework einfügen .. Es ist nicht verspottet und ich kann es nicht injizieren. Also, selbst wenn ich meinen gespotteten Kontext (IDataContext) passiere, wenn ich versuche, eine Entität einzufügen, die nicht verspottet wird, schlägt mein Test fehl!
Hier, dass Zweifel über ich habe eine falsche Architektur hat sich erhoben!
Also, was ist los? Die Architektur oder die Art, wie ich moq benutze?
Vielen Dank für Hilfe
UPDATE
Hier, wie ich meinen Code testen .. Zum Beispiel habe ich die Spur einer Zahlung testen möchten ..
Hier ist der Test:
[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
}
Hier ist die Fassade:
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;
}
Hier, wie ich die PaymentsManager
entwickelt:
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;
}
Am Ende, wie ich das Repository geschrieben:
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);
}
}
Könnten Sie uns bitte den Test zum Einfügen/Aktualisieren von Entitäten zeigen und erklären, wie genau dies fehlschlägt? Auch der zu testende Code wäre hilfreich. –
Ich habe meine Frage mit einem Beispiel aktualisiert – Ciccio