Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The repository itself is not usually tested?

I'm sorry but I'm new to repositories patterns, unit tests and orm tools.

I've been researching on unit tests and the repository pattern, and arrived at some conclusions, I wonder if I'm right.

The repository pattern facilitates unit testing to be replaced in controller that make use of it for example, right? Because create a stub/fake of context(in EF) or session(in NH) is harder, right? The repository itself is not tested? Why?

Using EntityFramework or NHibernate with repository pattern, if i want to test my repositories I need to do integration tests? Because if I use a fake implementation of my context/session I'm not doing real tests? Because the context/session itself is the repository (I mean they implement the real logic of Add, Remove, Edit, GetById, GetAll, ..)?

The repository pattern with EF or NH is like a wrapper? (Not only a wrapper, I know this is a import concept of the domain.)

like image 566
Acaz Souza Avatar asked Aug 18 '11 16:08

Acaz Souza


People also ask

What is repository in testing?

The test repository is a regular directory with a well-known structure, under source control, that stores all the test assets for a test automation project (tests, data files, scripts files, macros, etc.).

Should I write unit tests for repositories?

Unit testing Doctrine repositories is not recommended. Repositories are meant to be tested against a real database connection. However, in case you still need to do this, look at the following example.

Does repository need pattern?

The Repository pattern makes it easier to test your application logic. The Repository pattern allows you to easily test your application with unit tests. Remember that unit tests only test your code, not infrastructure, so the repository abstractions make it easier to achieve that goal.


3 Answers

Repository interface belongs to domain layer. But implementation belongs to infrastructure or data access layer. This implementation is usually not tested with unit test. It uses ORM API heavily, so it is extremely hard and wasteful to test repository in isolation. This problem is not specific to repositories: Don't mock types you don't own.

Repository should be tested with integration tests, using real ORM. In memory database is a very popular approach to this problem.

... Because if I use a fake implementation of my context/session I'm not doing real tests?

Even if you manage to make it (which I really doubt in NHibernate's case) you will be wasting your time. Session/Context interface is out of your control and your test would just reiterate your guess about how the real thing will work.

Becausethe context/session itself is the repository?

No. Context/Session is implementation of UnitOfWork pattern. It is not part of your domain. This is infrastructure.

The repository pattern with EF or NH is like a wrapper only?

Repository is important domain concept, it is not just a 'wrapper'. Just like your application is not a 'wrapper' over the database. I think that DDD repository interface definition should be based on Ubiquitous Language as much as possible and should not include any ORM related words or types.

like image 53
Dmitry Avatar answered Oct 25 '22 05:10

Dmitry


I would strictly differ between EF and NH in this case and I would not include both technologies in the same question. Simple NH is more mature and has architecture leading to code which can be more easily tested. Also in case of NH you can simply switch the database to another one (like SQLite) and it will work still the same which doesn't have to be true in case of EF where switching database can result in testing completely different application - especially if you switch between MS and non-MS database.

What is the repository? Let see Martin Fowler's definition:

A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection. Client objects construct query specifications declaratively and submit them to Repository for satisfaction. Objects can be added to and removed from the Repository, as they can from a simple collection of objects, and the mapping code encapsulated by the Repository will carry out the appropriate operations behind the scenes. Conceptually, a Repository encapsulates the set of objects persisted in a data store and the operations performed over them, providing a more object-oriented view of the persistence layer. Repository also supports the objective of achieving a clean separation and one-way dependency between the domain and data mapping layers.

Nice definition. Now think about the purpose of DbSet:

  • Does it act as in memory collection? Yes you can use it to get entities from database or use Local property to get already loaded entities.
  • Can client query specifications declaratively? Yes it is called linq-to-entities.
  • Can objects be added or removed as from collection? Yes it can.
  • Is the mapping encapsulated? Yes it is.
  • Is there clean separation? In terms of logic yes. In terms of API no because exposing IDbSet to domain model will make the domain model dependent on technology - EF. Is it problem? In theory yes, for purist yes but in 99% it is really not a problem because situation where you need to change the API is rare and it always involves big changes even if you correctly separated APIs.

DbSet is a repository. The only difference between using DbSet directly and wrapping it into some generic repository is a separation. This leads to my former answer to similar question - Generic Repository With EF 4.1 what is the point

Now what is a purpose of the repository in your application? I saw your previous questions including this one where you have your BaseRepository built on top of Entity framework. If you seriously mean this as a base repository which will be parent for your specialized repositories working on aggregate roots for your domain model and exposing specialized methods related only to specific exposed entity type then yes - you are using repository pattern and you need it. But if you are just wrapping context and single set and call this repository you most probably created only redundant layer with doubtful added value because that is just wrapper on top of DbSet.

There is only single scenario where your repository (DbSet wrapper) will make sense in such case:

  • The wrapper will never expose IQueryable (linq-to-entities)
  • The wrapper will never accept Expression<> and pass it internally to IQueryalbe (linq-to-entities)

This is the only scenario which will offer you fully mockable repositories => your upper layer can be easily unit tested. You are not going to unit test repositories and you are not going to mock context used in repositories. Repositories wrap data access and mapping logic - the only reasonable tests in case of repositories are integration tests. What is problem of this scenario? You will lose whole power of LINQ and you will have to wrap / re-implement some methods and types implemented in EF. This kind of repositories is the same as used when wrapping data access using stored procedures.

If you don't follow that scenario your live will be much easier. You will have queries defined by LINQ but you will not be able to unit test the code because there is no mock / fake which will still evaluate queries as linq-to-entities. Once you mock DbSet or IQueryable you will use linq-to-object which is superset of linq-to-entities. You can easily write a query which will pass a test on mocked DbSet but fail at runtime with a real DbSet. Here is more about this problem and here is the example of query which will pass the test but fail at runtime. In this case you must use integration tests (with real database) for all methods using linq-to-entities queries on top of your repositories.

like image 39
Ladislav Mrnka Avatar answered Oct 25 '22 03:10

Ladislav Mrnka


It's pretty common to make repositories which are just lightweight wrappers of your DB access and put business methods in an entity service layer which depends on the repositories, yes. So you can unit test the entity services by using repositories which wrap, e.g., an in-memory DB.

like image 39
Craig Stuntz Avatar answered Oct 25 '22 04:10

Craig Stuntz