How to trick Entity Framework into N-tier architecture

I have an N-tier application with Entity Framework (Code-First approach). Now I want to automate some tests. I am using the Moq infrastructure . I find the problem with writing tests. Perhaps my architecture is wrong? With the wrong one, I mean that I wrote components that are not sufficiently isolated, and therefore they are not tested. I don't really like this ... Or maybe I just can't use the moq framework properly.

Let me see my architecture:

enter image description here

At each level, I insert my context into the constructor of the class.

Facade:

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

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 is the interface implemented by my DbContext:

 public partial class PublicAreaContext : DbContext, IDataContext 

Now, how do I layout EF and how do I write tests:

 [TestInitialize] public void Init() { this._mockContext = ContextHelper.CreateCompleteContext(); } 

Where 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; } 

And here is how I write my 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 } 

It seems that everything is working correctly! And it looks like my architecture is correct. But what if I want to insert / update Entity? Nothing else works! I explain why:

As you can see, I pass the *Request object (this is a DTO) to the facade, and then in my TOA I generate my entity from the correctness of the 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 is the Entity that I insert in the Entity Framework. This is not mocking, and I can not enter it. Therefore, even if I pass my mocked context (IDataContext), when I try to insert an object that is not mocking, my test does not work!

This doubt is that my wrong architecture has risen!

So what happened? Architecture or method of using moq? A.

thanks for the help

UPDATE

This is how I check my code. For example, I want to check the payment trace.

Here's the 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 } 

Here is the facade:

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

Here is how I developed 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; } 

In the end, as I wrote the repository:

 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); } } 
+5
source share
4 answers

Your architecture looks good, but the implementation is wrong. This is a leak of abstraction.

In your diagram, the Facade level depends only on the BLL , but when you look at the PublicAreaFacade constructor, you will see that in fact it directly depends on the interface from the Repository level:

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

It should not be. It should only accept its direct dependency as input - PaymentsManager or - even better - the interface of this:

 public PublicAreaFacade(IPaymentsManager paymentsManager) { ... } 

The behavior is that your code becomes more verifiable. When you look at your tests now, you will see that you have to mock the innermost level of your system (i.e., IDataContext and even its Set<TEntity> access objects), although you are testing one of the outermost levels of your system (class PublicAreaFacade ).

Here's what unit test looks like for the TracePaymentAttempt method, if PublicAreaFacade depends only on 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>())) } 
+2
source

Pass IUnitOfWork to the Facade or BLL layer constructor, depending on what calls the unit of work calls directly. Then you can customize what the Mock<IUnitOfWork> returns in your tests. You do not need to pass IDataContext everyone except repo constructors and units of work.

For example, if Facade has a PrepareEasyPayPayment method that makes a repo call through a UnitOfWork call, configure the layout like this:

 // Arrange var unitOfWork = new Mock<IUnitOfWork>(); unitOfWork.Setup(x => x.PrepareEasyPayPaymentRepoCall(request)).Returns(true); var paymentFacade = new PaymentFacade(unitOfWork.Object); // Act var result = paymentFacade.PrepareEasyPayPayment(request); 

Then you mock the data call and can more easily test your code in Facade.

To test the insertion, you must have a Facade method, such as CreatePayment , which accepts PrepareEasyPayPaymentRequest . Inside this CreatePayment method, it should reference the repo, possibly through a unit of work, for example

 var result = _unitOfWork.CreatePaymentRepoCall(request); if (result == true) { // yes! } else { // oh no! } 

What you want to mock unit testing is that the create / insert repo call returns true or false, so you can test the code branches after the repo call is completed.

You can also verify that the insert call was made as expected, but this is usually not so valuable if the parameters for this call do not have a lot of logic involved in building them.

0
source

It looks like you need to change the code a bit. New things introduce hard-coded dependencies and make them unstable, so try to abstract them. Perhaps you can hide everything related to EF behind another layer, then all you need to do is to mock this layer level and never touch EF.

0
source

You can use this open source environment for unit testing, which is good for faking the framework dbcontext entity

https://effort.codeplex.com/

Try it, it will help you effectively mock your data.

0
source

All Articles