5

I have a N-Layer application with Entity Framework (Code-First approach). Now I want to automatize some tests. I am using Moq framework. I am finding some problem about writing the tests. Perhaps my architecture is wrong? With wrong, I mean that I wrote components that are not well isolated and so they are not testable. I do not really like this... Or perhaps, I simply cannot use correctly moq framework.

I let you see my architecture:

enter image description here

At every level I inject my context in the constructor of the class.

The Facade:

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

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

The BLL:

public abstract class BaseManager
{
    protected IDataContext Context;

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

The 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 an interface that is implemented by my DbContext:

public partial class PublicAreaContext : DbContext, IDataContext

Now, how I mock EF and how I write the tests:

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

Where ContextHelper.CreateCompleteContext() is:

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

Here It seems It works everything correctly!!! And It looks like my architecture is correct. But what if I want to insert/update an Entity? Nothing work anymore! I explain why:

As you can see I pass a *Request object (it is the DTO) to the facade, then in my TOA I generate my entity from the propertiess 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 I will inserto to Entity Framework.. It is not mocked and I cannot inject it. So even if I pass my mocked context (IDataContext), when I try to insert an Entity that is not mocked my test fails!

Here that doubt about I have a wrong architecture has raised!

So, what's wrong? The architecture or the way I use moq?

Thank you for help

UPDATE

Here how I test my code.. For example, I want to test the trace of a payment..

Here 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 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 how I developed the 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 how 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);
    }
}
Ciccio
  • 1,703
  • 3
  • 23
  • 62

4 Answers4

2

Your architecture looks good, but the implementation is flawed. It is leaking abstraction.

In your diagram the Façade layer depends only on the BLL but when you look at the PublicAreaFacade's constructor you will see that in reality it has a direct dependency to an interface from the Repository layer:

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

This should not be. It should only take its direct dependency as input -- the PaymentsManager or -- even better -- an interface of it:

public PublicAreaFacade(IPaymentsManager paymentsManager)
{
    ...
}

The concequence is that your code becomes way more testable. When you look at your tests now you see that you have to mock the most inner layer of your system (i.e. the IDataContext and even its entity accessors Set<TEntity>) altough you are testing one of the most outer layers of your system (the PublicAreaFacade class).

This is how a unit test for the TracePaymentAttempt method would look like if the PublicAreaFacade only depended 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>()))
}
Good Night Nerd Pride
  • 6,287
  • 3
  • 38
  • 54
  • The `unitOfWork` contains all necessary manager that are instanciate in a lazy way.. About the `IDataContext` I agree with you, I do not like that I pass it inside the facade, but if I do not pass it I have 2 problems: 1. How can I mock the context, if the context is "hidden" behind the the `BLL`? If you see my tests, I mock the context and then I pass it in input to the facade; 2. 1 Facade can call different managers. Managers use the context. If I do not pass the context in input to facade, how can I instanciate just one context for each manager? – Ciccio May 26 '16 at 08:55
  • 1
    1) The trick is that you don't have to mock the context when testing a facade. If you test a method of a facade you only want to test what the facade itself is doing but not what is happening implicitly in the background -- that's the very definition of mocking. – Good Night Nerd Pride May 26 '16 at 09:06
  • 1
    2) What I'm suggesting you to do is called *dependency injection*. In DI you usually create the complete object graph in the entry point of the application. In your case that's the REST service endpoint. But you can also use a DI framework like Micorsoft's Unity which makes object graph creation much, much easier. – Good Night Nerd Pride May 26 '16 at 09:09
  • Thank you, I am already using Microsoft's Unity, but surely I am not still really practiced... Even if it works, I know there is something wrong in my configuration of Unity but I have not decided yet which is the best way to correct it... – Ciccio May 26 '16 at 09:13
0

Pass IUnitOfWork into the Facade or BLL layer constructor, whichever one makes calls on the unit of work directly. Then you can setup what the Mock<IUnitOfWork> is returning in your tests. You should not need to pass IDataContext to everything except maybe the repo constructors and the unit of work.

For example, if the Facade has a method PrepareEasyPayPayment that makes a repo call through a UnitOfWork call, setup the mock 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've mocked out the data call and can more easily test your code in the Facade.

For the insert testing, you should have a Facade method like CreatePayment which takes a PrepareEasyPayPaymentRequest. Inside that CreatePayment method, it should reference the repo, probably through the unit of work, like

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

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

You can also test that the insert call was made as expected, but that's usually not as valuable unless the parameters for that call have a lot of logic involved in building them.

Joe Wilson
  • 5,280
  • 2
  • 23
  • 34
  • I understand... but unlckly your solution do not solve my problem.. I already have a method like `CreatePayment` which tacke a `PrepareEasyPayPaymentRequest`. I can change my facade to pass `IUnitOfWork` into the Facade constructor.. but the problem remains! The problem is that inside the Facade I create the Entity (of Entity Framework) to insert inside the database. I instanciate the entity... I do a `new`... In this way the entity is not mocked... so I cannot test the insert.. It's like I shoul pass to Facade the mocked entity, but surely this is not the correct way to solve the prob – Ciccio May 26 '16 at 07:31
0

it sounds like you need to change the code a little bit. Newing things introduces hardcoded dependencies and makes them untestable, so try to abstract them away. Maybe you can hide everything to do with EF behind another layer, then all you have to do is mock that particular layer layer and never touch EF.

Andrei Dragotoniu
  • 5,633
  • 3
  • 16
  • 30
0

You can use this open source framework for unit testing which is good to mock entity framework dbcontext

https://effort.codeplex.com/

Try this will help you to mock your data efficiently.

Mayank Jain
  • 205
  • 2
  • 6