How can I mock a DataServiceQuery for unit testing purpose?
Long Details follow:
Imagine an ASP.NET MVC application, where the controller talks to an ADO.NET DataService that encapsulates the storage of our models (for example sake we'll be reading a list of Customers). With a reference to the service, we get a generated class inheriting from DataServiceContext:
namespace Sample.Services
{
public partial class MyDataContext : global::System.Data.Services.Client.DataServiceContext
{
public MyDataContext(global::System.Uri serviceRoot) : base(serviceRoot) { /* ... */ }
public global::System.Data.Services.Client.DataServiceQuery<Customer> Customers
{
get
{
if((this._Customers==null))
{
this._Customers = base.CreateQuery<Customer>("Customers");
}
return this._Customers;
}
}
/* and many more members */
}
}
The Controller could be:
namespace Sample.Controllers
{
public class CustomerController : Controller
{
private IMyDataContext context;
public CustomerController(IMyDataContext context)
{
this.context=context;
}
public ActionResult Index() { return View(context.Customers); }
}
}
As you can see, I used a constructor that accepts an IMyDataContext instance so that we can use a mock in our unit test:
[TestFixture]
public class TestCustomerController
{
[Test]
public void Test_Index()
{
MockContext mockContext = new MockContext();
CustomerController controller = new CustomerController(mockContext);
var customersToReturn = new List<Customer>
{
new Customer{ Id=1, Name="Fred" },
new Customer{ Id=2, Name="Wilma" }
};
mockContext.CustomersToReturn = customersToReturn;
var result = controller.Index() as ViewResult;
var models = result.ViewData.Model;
//Now we have to compare the Customers in models with those in customersToReturn,
//Maybe by loopping over them?
foreach(Customer c in models) //*** LINE A ***
{
//TODO: compare with the Customer in the same position from customersToreturn
}
}
}
MockContext and MyDataContext need to implement the same interface IMyDataContext:
namespace Sample.Services
{
public interface IMyDataContext
{
DataServiceQuery<Customer> Customers { get; }
/* and more */
}
}
However, when we try and implement the MockContext class, we run into problems due to the nature of DataServiceQuery (which, to be clear, we're using in the IMyDataContext interface simply because that's the data type we found in the auto-generated MyDataContext class that we started with). If we try to write:
public class MockContext : IMyDataContext
{
public IList<Customer> CustomersToReturn { set; private get; }
public DataServiceQuery<Customer> Customers { get { /* ??? */ } }
}
In the Customers getter we'd like to instantiate a DataServiceQuery instance, populate it with the Customers in CustomersToReturn, and return it. The problems I run into:
1~ DataServiceQuery has no public constructor; to instantiate one you should call CreateQuery on a DataServiceContext; see MSDN
2~ If I make the MockContext inherit from DataServiceContext as well, and call CreateQuery to get a DataServiceQuery to use, the service and query have to be tied to a valid URI and, when I try to iterate or access the objects in the query, it will try and execute against that URI. In other words, if I change the MockContext as such:
namespace Sample.Tests.Controllers.Mocks
{
public class MockContext : DataServiceContext, IMyDataContext
{
public MockContext() :base(new Uri("http://www.contoso.com")) { }
public IList<Customer> CustomersToReturn { set; private get; }
public DataServiceQuery<Customer> Customers
{
get
{
var query = CreateQuery<Customer>("Customers");
query.Concat(CustomersToReturn.AsEnumerable<Customer>());
return query;
}
}
}
}
Then, in the unit test, we get an error on the line marked as LINE A, because http://www.contoso.com doesn't host our service. The same error is triggered even if LINE A tries to get the number of elements in models.
Thanks in advance.
I solved this by creating an interface IDataServiceQuery with two implementations:
DataServiceQueryWrapper
MockDataServiceQuery
I then use IDataServiceQuery wherever I would have previously used a DataServiceQuery.
public interface IDataServiceQuery<TElement> : IQueryable<TElement>, IEnumerable<TElement>, IQueryable, IEnumerable
{
IDataServiceQuery<TElement> Expand(string path);
IDataServiceQuery<TElement> IncludeTotalCount();
IDataServiceQuery<TElement> AddQueryOption(string name, object value);
}
The DataServiceQueryWrapper takes a DataServiceQuery in it's constructor and then delegates all functionality to the query passed in. Similarly, the MockDataServiceQuery takes an IQueryable and delegates everything it can to the query.
For the mock IDataServiceQuery methods, I currently just return this, though you could do something to mock the functionality if you want to.
For example:
// (in DataServiceQueryWrapper.cs)
public IDataServiceQuery<TElement> Expand(string path)
{
return new DataServiceQueryWrapper<TElement>(_query.Expand(path));
}
// (in MockDataServiceQuery.cs)
public IDataServiceQuery<TElement> Expand(string path)
{
return this;
}
[Disclaimer - I work at Typemock]
Have you considered using a mocking framework?
You can use Typemock Isolator to create a fake instance of DataServiceQuery:
var fake = Isolate.Fake.Instance<DataServiceQuery>();
And you can create a similar fake DataServiceContext and set it's behavior instead of trying to inherit it.
Related
I need some help. I'm using ASP.NET MVC which I am beginner in.
I'm writing an application with a database and 3 tables (2 tables are just for parent child drop downs, and third is for saving data from dropdowns and fill other forms).
I am using Entity Framework with SQL to connect my database to ASP.NET MVC with autogenerated models from database.
I manually make ViewModel of all three tables and their fields and I need to pass all data to 1 view (Create View)
Here is my code from Home controller where I am getting error.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(CountryStateContactsViewModel csvm)
{
if (!ModelState.IsValid)
{
return View(csvm);
}
// Error happens here
Contact contactModel = Mapper.Map<CountryStateContactsViewModel, Contact>(csvm);
db.Contacts.Add(contactModel);
db.SaveChanges();
return RedirectToAction("Index");
}
This is the error I get:
An object reference is required for the non-static field, method, or property 'Mapper.Map<CountryStateContactsViewModel, Contact>(CountryStateContactsViewModel)
In order to use AutoMapper , first you should CreateMap between your classes.
public class ContactProfile {
public AutoMapperProfile() {
CreateMap<CountryStateContactsViewModel,Contact>();
}
}
public class AutoMapperConfiguration {
public static void Initialize() {
Mapper.Initialize(cfg = > {
cfg.AddProfile(new ContactProfile());
});
}
}
And finally in your Global.asax:
AutoMapperConfiguration.Initialize()
According to OP comments there is no AutoMapper configuration, and without it AutoMapper cannot resolve mappings.
Define an interface to abstract the mapping methods:
public interface IMappingService
{
TDest Map<TSrc, TDest>(TSrc source) where TDest : class;
TDest Map<TSrc, TDest>(TSrc source, TDest dest) where TDest : class;
}
Implement the interface:
public class MappingService : IMappingService
{
private MapperConfiguration mapperConfiguration;
private IMapper mapper;
public MappingService()
{
mapperConfiguration = new MapperConfiguration(cfg =>
{
// Define here your mapping profiles...
cfg.AddProfile<ViewModelToDomainMappingProfile>();
cfg.AddProfile<DomainToViewModelMappingProfile>();
});
// You may not want to assert that your config is valid, and that's ok.
mapperConfiguration.AssertConfigurationIsValid();
mapper = mapperConfiguration.CreateMapper();
}
public TDest Map<TSrc, TDest>(TSrc source) where TDest : class
{
return mapper.Map<TSrc, TDest>(source);
}
public TDest Map<TSrc, TDest>(TSrc source, TDest dest) where TDest : class
{
return mapper.Map(source, dest);
}
}
Now you have to define your profiles (example):
public class ViewModelToDomainProfile: Profile
{
public ViewModelToDomainProfile()
{
CreateMap<CountryStateContactsViewModel, Contact>();
}
}
public class DomainToViewModelProfile: Profile
{
public DomainToViewModelProfile()
{
CreateMap<CountryStateContactsViewModel, Contact>();
}
}
Finally, inject your IMappingService in your controller:
private readonly IMappingService _mappingService;
public HomeController(IMappingService mappingService) {
_mappingService = mappingService;
}
And use it like so:
_mappingService.Map<CountryStateContactsViewModel, Contact>(viewModel);
I like this solution because it encapsulates everything nicely.
EDIT: #Arsalan Valoojerdi was faster than me. But, this way you have two different approaches.
Note: Do not forget to define your dependency to IMappingService on your IoC container (eg. Ninject).
Is it possible to pass into the ModelBinder which implementation you want to use inline?
Given the following definitions:
public interface ISomeInterface
{
string MyString{get;set;}
}
public class SomeInterfaceImplementation_One : ISomeInterface
{
private string _MyString;
public string MyString
{
get {return "This is implementation One " + _MyString ; }
set { _MyString = value; }
}
}
public class SomeInterfaceImplementation_Two : ISomeInterface
{
private string _MyString;
public string MyString
{
get {return "This is implementation Two" + _MyString ; }
set { _MyString = value; }
}
}
Given this route in asp.net mvc core:
public ActionResult InterfaceWithInlineImplementation([ModelBinder(typeof(SomeBinder))]ISomeInterface SomeInterface)
{
//Return actionresult
}
I do not want a different ModelBinder class for each implementation rather I would like each route to specify which implementation inline.
So something like:
[UseImplementation(SomeInterfaceImplementation_One)]
public ActionResult InterfaceWithInlineImplementation([ModelBinder(typeof(SomeBinder))]ISomeInterface SomeInterface)
{
}
Or:
public ActionResult InterfaceWithInlineImplementation([ModelBinder(typeof(SomeBinder), ConcreteType = SomeInterfaceImplementation_Two )]ISomeInterface SomeInterface)
{
}
This way the SomeBinder class can access which implementation is being requested in the BindModelAsync method of SomeBinder : IModelBinder class.
public class SomeBinder : Microsoft.AspNetCore.Mvc.ModelBinding.IModelBinder
{
public Task BindModelAsync(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingContext bindingContext)
{
if (bindingContext == null)
throw new ArgumentNullException(nameof(bindingContext));
string valueFromBody = string.Empty;
using (var sr = new StreamReader(bindingContext.HttpContext.Request.Body))
{
valueFromBody = sr.ReadToEnd();
}
if (string.IsNullOrEmpty(valueFromBody))
{
return Task.CompletedTask;
}
var settings = new JsonSerializerSettings()
{
ContractResolver = new InterfaceContractResolver(), // Need requested implementation from InterfaceWithInlineImplementation() method
};
var obj = JsonConvert.DeserializeObject(valueFromBody, [**Need Requested Implementation from Method**], settings);
bindingContext.Model = obj;
bindingContext.Result = ModelBindingResult.Success(obj);
return Task.CompletedTask;
}
Use generics.
public class SomeBinder<TConcreteType> : IModelBinder
{
}
Then your signature becomes
public ActionResult InterfaceWithInlineImplementation(
[ModelBinder(typeof(SomeBinder<SomeInterfaceImpelemtation_One>))]ISomeInterface SomeInterface)
Then deserialization is:
JsonConvert.DeserializeObject<TConcreteType>(json)
However based on your last comment it sounds like you just need to Prevent overposting instead of this convoluted model binding.
So lets say the client knows that the server implementation has security methods and tries to match the signature hoping everything get deseriazled for example. Its being explicit as to what you're expecting. And you're explicitly expecting only the contract definition and nothing more.
Excerpt:
Mass assignment typically occurs during model binding as part of MVC. A simple example would be where you have a form on your website in which you are editing some data. You also have some properties on your model which are not editable as part of the form, but instead are used to control the display of the form, or may not be used at all.
public class UserModel
{
public string Name { get; set; }
public bool IsAdmin { get; set; }
}
So the idea here is that you only render a single input tag to the markup, but you post this to a method that uses the same model as you used for rendering:
[HttpPost]
public IActionResult Vulnerable(UserModel model)
{
return View("Index", model);
}
However, with a simple bit of HTML manipulation, or by using Postman/Fiddler , a malicious user can set the IsAdmin field to true. The model binder will dutifully bind the value, and you have just fallen victim to mass assignment/over posting:
So how can you prevent this attack? Luckily there's a whole host of different ways, and they are generally the same as the approaches you could use in the previous version of ASP.NET. I'll run through a number of your options here.
Continue to article...
We have ASP MVC web project. After reading a lot of articles and discussions here in stackoverflow about the correct architechture we have decided to go with the following one, although there is not only one correct way of doing things this is the way we have decided, but we still have some doubts.
We are publishing this here not only to be helped but also to show what we have done in case it is helpful to somebody.
We are working in ASP .NET MVC project, EF6 Code first with MS SQL Server.
We have divided the project into 3 main layers that we have separate into 3 projects: model, service and web.
The model creates the entities and setup the DataContext for the database.
The service make the queries to the data base and transform those entities into DTOs to pass them to the web layer, so the web layer doesn't know anything about the database.
The web uses AutoFac for the DI (dependency Injection) to call the services we have in the service layer and obtain the DTOs to transform those DTOs into Model Views to use them in the Views.
After reading a lot of articles we decided not to implement a repository pattern and unit of work because, in summary, we have read the EF acts as a unit of work itself. So we are simplifying things a little here.
https://cockneycoder.wordpress.com/2013/04/07/why-entity-framework-renders-the-repository-pattern-obsolete/
This is the summary of our project. Now I'm going to go through every project to show the code. We are going to show only a couple of entities, but our project has more than 100 different entities.
MODEL
Data Context
public interface IMyContext
{
IDbSet<Language> Links { get; set; }
IDbSet<Resources> News { get; set; }
...
DbSet<TEntity> Set<TEntity>() where TEntity : class;
DbEntityEntry<TEntity> Entry<TEntity>(TEntity entity) where TEntity : class;
}
public class MyDataContext : DbContext, IMyContext
{
public MyDataContext() : base("connectionStringName")
{
}
public IDbSet<Language> Links { get; set; }
public IDbSet<Resources> News { get; set; }
...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Properties<DateTime>().Configure(c => c.HasColumnType("datetime2"));
}
}
Here is how we declare the entities
public class Link
{
public int Id{ get; set; }
public string Title { get; set; }
public string Url { get; set; }
public bool Active { get; set; }
}
SERVICES
These are the generic classes we use for all the services.
As you see we use the DTOs to get data from the web layer. Also we connect to the database using Dbset = Context.Set()
public interface IService
{
}
public interface IEntityService<TDto> : IService where TDto : class
{
IEnumerable<TDto> GetAll();
void Create(TDto entity);
void Update(TDto entity);
void Delete(TDto entity);
void Add(TDto entity);
void Entry(TDto existingEntity, object updatedEntity);
void Save();
}
public abstract class EntityService<T, TDto> : IEntityService<TDto> where T : class where TDto : class
{
protected IClientContext Context;
protected IDbSet<T> Dbset;
protected EntityService(IClientContext context) { Context = context; Dbset = Context.Set<T>(); }
public virtual IEnumerable<TDto> GetAll()
{
return Mapper.Map<IEnumerable<TDto>>(Dbset.AsEnumerable());
}
public virtual void Create(TDto entity)
{
if (entity == null)
{
throw new ArgumentNullException(nameof(entity));
}
Dbset.Add(Mapper.Map<T>(entity));
Context.SaveChanges();
}
public virtual void Update(TDto entity)
{
if (entity == null) throw new ArgumentNullException(nameof(entity));
Context.Entry(entity).State = EntityState.Modified;
Context.SaveChanges();
}
public virtual void Delete(TDto entity)
{
if (entity == null) throw new ArgumentNullException(nameof(entity));
Dbset.Remove(Mapper.Map<T>(entity));
Context.SaveChanges();
}
public virtual void Add(TDto entity)
{
Dbset.Add(Mapper.Map<T>(entity));
}
public virtual void Entry(TDto existingEntity, object updatedEntity)
{
Context.Entry(existingEntity).CurrentValues.SetValues(updatedEntity);
}
public virtual void Save()
{
Context.SaveChanges();
}
}
We declare the DTOs in this project (this is a very simple example so we don't have to put all the code here):
public class LinkDto
{
public int Id { get; set; }
public string Title { get; set; }
public string Url { get; set; }
public bool Active { get; set; }
}
Then one of our services:
public interface ILinkService : IEntityService<LinkDto>
{
IPagedList<LinkDto> GetAllLinks(string searchTitle = "", bool searchActive = false, int pageNumber = 1, int pageSize = 10);
LinkDto FindById(int id);
LinkDto Test();
}
public class LinkService : EntityService<Link, LinkDto>, ILinkService
{
public LinkService(IClientContext context) : base(context) { Dbset = context.Set<Link>(); }
public virtual IPagedList<LinkDto> GetAllLinks(bool searchActive = false, int pageNumber = 1, int pageSize = 10)
{
var links = Dbset.Where(p => p.Active).ToPagedList(pageNumber, pageSize);
return links.ToMappedPagedList<Link, LinkDto>();
}
public virtual LinkDto FindById(int id)
{
var link = Dbset.FirstOrDefault(p => p.Id == id);
return Mapper.Map<LinkDto>(link);
}
public LinkDto Test()
{
var list = (from l in Context.Links
from o in Context.Other.Where(p => p.LinkId == l.Id)
select new OtherDto
{ l.Id, l.Title, l.Url, o.Other1... }).ToList();
return list;
}
}
As you see we use AutoMapper (version 5 which has changed a little) to transform from Entities to DTOs the data.
One of the doubts we have is if the use of "Dbset.Find" or "Dbset.FirstOrDefault" is correct and also if the use of "Context.Links" (for any entity).
WEB
FInally the web project where we receive the DTOs and transform those DTOs into ModelViews to show in our views.
We need to call, in the Global.asax Application_Start, AutoFac to do the DI so we can use our services.
protected void Application_Start()
{
...
Dependencies.RegisterDependencies();
AutoMapperBootstrapper.Configuration();
...
}
public class Dependencies
{
public static void RegisterDependencies()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly).PropertiesAutowired();
builder.RegisterModule(new ServiceModule());
builder.RegisterModule(new EfModule());
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
public class ServiceModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterAssemblyTypes(Assembly.Load("MyProject.Service")).Where(t => t.Name.EndsWith("Service")).AsImplementedInterfaces().InstancePerLifetimeScope();
}
}
public class EfModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType(typeof(MyDataContext)).As(typeof(IMyContext)).InstancePerLifetimeScope();
}
}
As you see we also call AutoMapper to configure the different maps.
Then in our controllers we have this.
public class LinksController : Controller
{
private readonly ILinkService _linkService;
public LinksController(ILinkService linkService)
{
_linkService = linkService;
}
public ActionResult Index()
{
var links = _linkService.GetAllLinks();
return View(links.ToMappedPagedList<LinkDto, LinksListModelAdmin>());
}
...
public ActionResult Create(LinksEditModelAdmin insertedModel)
{
try
{
if (!ModelState.IsValid) return View("Create", insertedModel);
var insertedEntity = Mapper.Map<LinkDto>(insertedModel);
_linkService.Create(insertedEntity);
return RedirectToAction("Index");
}
catch (Exception ex)
{
throw ex;
}
}
}
Well, this is it...I hope this can be useful for somebody...and also I hope we can have a little help with the questions we have.
1) Although we are separating database from the web project we do need a reference in the web project to initialize the database and also to inject dependencies, is this correct?
2) Is it correct the approach we have done having our Entities->DTOs->ViewModels? It's a little more work but we have everything separated.
3) In the Service project, when we need to reference a different entity than the main one we are using in the service, is it correct to call Context.Entity?
For example, if we need to retrieve also data from the News entity in the links service, is it correct to call "Context.News.Where..."?
4) We do have a little problem with Automapper and EF proxy, because when we call "Dbset" to retrieve data, it gets a "Dynamic proxies" object so Automapper can't find the proper map so, in order to work, we have to set ProxyCreationEnabled = false in the DataContext definition. This way we can get an Entity in order to map it to the DTO. This disables LazyLoading, which we don't mind, but is this a correct approach or there is a better way to solve this?
Thanks in advance for your comments.
For Question no. 2
Entities->DTOs->ViewModels? is good approach
because you are doing the clean separation, the programmer can work together with ease.
The person who design ViewModels, Views and Controllers don't have to worry about the service layer or the DTO implementation because he will make the mapping when the others developpers finish their implementation.
For Question no. 4
When the flag ProxyCreationEnabled is set to false, the proxy instance will not be created with creating a new instance of an entity. This might not be a problem but we can create a proxy instance using the Create method of DbSet.
using (var Context = new MydbEntities())
{
var student = Context.StudentMasters.Create();
}
The Create method has an overloaded version that accepts a generic type. This can be used to create an instance of a derived type.
using (var Context = new MydbEntities())
{
var student = Context.StudentMasters.Create<Student>();
}
The Create method just creates the instance of the entity type if the proxy type for the entity would have no value (it is nothing to do with a proxy). The Create method does not add or attach the entity with the context object.
Also i read some where if you set ProxyCreationEnabled = false the child element will not loaded for some parent object unless Include method is called on parent object.
I've been looking at a few blog posts to try and create an appropriate solution for the following requirements but I can't seem to piece them together. Hope fully someone can help.
I've been using Repository pattern with interfaces using Automapper...here's a trimmed down example:
public class BookingRepository : IBookingRepository
{
Entities context = new Entities();
public IEnumerable<BookingDto> GetBookings
{
get { return Mapper.Map<IQueryable<Booking>, IEnumerable<BookingDto>>(context.Bookings); }
}
public BookingDto GetBookingWithProduct(Guid bookingId)
{
return Mapper.Map<BookingDto>(context.Bookings.Include(c => c.Products).SingleOrDefault(c => c.BookingId == bookingId));
}
public void Update(BookingDto bookingDto)
{
var booking = Mapper.Map<Booking>(bookingDto);
context.Entry(booking).State = EntityState.Modified;
}
public void Save()
{
context.SaveChanges();
}
public void Dispose()
{
context.Dispose();
}
}
public interface IBookingRepository : IDisposable
{
IEnumerable<BookingDto> GetBookings { get; }
BookingDto GetBooking(Guid bookingId);
void Update(BookingDto bookingDto);
void Save();
}
With a seperate Repository for a different Entity, for example
public class ProductRepository : IProductRepository
{
Entities context = new Entities();
public IEnumerable<ProductDto> GetProducts
{
get { return Mapper.Map<IQueryable<Product>, IEnumerable<ProductDto>>(context.Products); }
}
public ProductDto GetProductWithDesign(int productId)
{
return Mapper.Map<ProductDto>(context.Products.Include(c => c.Designs).SingleOrDefault(c => c.ProductId == productId));
}
public void Update(ProductDto productDto)
{
var product = Mapper.Map<Product>(productDto);
context.Entry(product).State = EntityState.Modified;
}
public void Save()
{
context.SaveChanges();
}
public void Dispose()
{
context.Dispose();
}
}
public interface IProductRepository : IDisposable
{
IEnumerable<ProductDto> GetProducts { get; }
ProductDto GetProduct(int productId);
void Update(ProductDto productDto);
void Save();
}
Then in my Controller I'm using the repositories as so:
public class HomeController : Controller
{
private readonly IBookingRepository bookingRepository;
private readonly IProductRepository productRepository;
public HomeController() : this(new BookingRepository(), new ProductRepository()) { }
public HomeController(IBookingRepository bookingRepository, IProductRepository productRepository)
{
this.bookingRepository = bookingRepository;
this.productRepository = productRepository;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing && this.bookingRepository != null)
this.bookingRepository.Dispose();
if (disposing && this.productRepository != null)
this.productRepository.Dispose();
}
}
So now I'm hoping to create a Unit Of Work to abstract these repositories and share the context and also create a generic repository for the duplicated actions (Save and Update) bearing in mind I'm passing in Dtos and Mapping to Entity objects. I'm having difficulty understanding how to knit it all together.
Additionally, I've seen this post
Repository pattern with generics and DI
which states "You should not have other repository interfaces besides your generic repository" and that custom queries "deserve their own (generic) abstraction:" which is adding another complication to my overworked brain as my repositories will have custom queries that return complex linked objects using Include Statements as Lazy Loading is disabled.
So I'm prepared to be shot down and told that I'm going about this the wrong way but would be grateful for any direction given.
Thanks in advance.
Don't use generic repositories. They are all leaky abstractions. Ask yourself, what benefit to you get by using an abstraction that doesn't really abstract away something? You could use your OR/M directly in those cases.
What I means is that anything that exposes IQueryable<T> forces the user to learn about the weaknesses that the underlying OR/M has. Examples: How do the orm handle lazy loading? How do I eagerly load related entities? How do I create a IN clause?
If you truly want to use the repository pattern either use it together with the specification pattern (you can keep on using a generic repository then) or create repositories that are specific for each root aggregate.
I've blogged about it: http://blog.gauffin.org/2013/01/repository-pattern-done-right/
What I usually do in this case is to create a Base abstract Repository class like this:
public abstract class BaseRepository<T> : IRepository<T>
{
Entities context = new Entities();
public virtual T GetAll()
{
return context.Set<T>();
}
// Add base implementation for normal CRUD here
}
If you don't need special queries then you don't need to create special interface and classes (but you can of course, to improve readability). So you will use, for example:
var bookingsRepo = new BaseRepository<BookingsDto>();
var allBookings = bookingsRepo.GetAll();
If you need some special queries, you create an interface that extends the base interface:
public interface IProductRepository : IRepository<Product>
{
Product GetSpecialOffer();
}
Then create your class:
public class ProductRepository : BaseRepository<Product>, IProductRepository
{
public Product GetSpecialOffer()
{
// your logic here
}
}
That way you only specify a minimal number of special cases while relying on the Base abstract implementation for all things normal.
I added virtual to the base methods because I always like to give derived class the ability to override stuff...
I'm getting my feet wet with unit testing (TDD). I have a basic repository pattern I'm looking to test and I'm not really sure I'm doing things correctly. At this stage I'm testing my domain and not worrying about controllers and views. To keep it simple here is a demo project.
Class
public class Person
{
public int PersonID { get; set; }
public string Name{ get; set; }
}
Interface
public interface IPersonRepository
{
int Add(Person person);
}
Concrete
public class PersonnRepository : IPersonRepository
{
DBContext ctx = new DBContext();
public int Add(Person person)
{
// New entity
ctx.People.Add(person);
ctx.SaveChanges();
return person.id;
}
}
I've added NUnit and MOQ to my test project and want to know how to properly test the functionality.
I'm not sure it's right but after reading some blogs I ended up creating a FakeRepository, however if I test based on this, how is that validating my actual interface?
public class FakePersonRepository
{
Dictionary<int, Person> People = new Dictionary<int, Person>();
public int Add(Person person)
{
int id = People.Count + 1;
People.Add(id, person);
return id;
}
}
then tested with
[Test]
public void Creating_A_Person_Should_Return_The_ID ()
{
FakePersonRepository repository = new FakePersonRepository();
int id = repository.Add(new Person { Name = "Some Name" });
Assert.IsNotNull(id);
}
Am I anywhere close to testing in the correct manor?
I'd like to test things like not passing a name causes error etc in the future.
Am I anywhere close to testing in the correct manor?
I am afraid that you are not. The idea of having an interface is that it allows you to decouple other code that uses a repository such your controller and be able to unit test it in isolation. So let's suppose that you have the following controller that you want to unit test:
public class PersonController : Controller
{
private readonly IPersonRepository _repo;
public PersonController(IPersonRepository repo)
{
_repo = repo;
}
[HttpPost]
public ActionResult Create(Person p)
{
if (!ModelState.IsValid)
{
return View(p);
}
var id = _repo.Add(p);
return Json(new { id = id });
}
}
Notice how the controller doesn't depend on a specific repository implementation. All needs is that this repository implements the given contract. Now we could use a mocking framework such as Moq in the unit test to provide a fake repository and make it behave as we like in order to test the 2 possible paths in the Create action:
[TestMethod]
public void PersonsController_Create_Action_Should_Return_View_And_Not_Call_Repository_If_ModelState_Is_Invalid()
{
// arrange
var fakeRepo = new Mock<IPersonRepository>();
var sut = new PersonController(fakeRepo.Object);
var p = new Person();
sut.ModelState.AddModelError("Name", "The name cannot be empty");
fakeRepo.Setup(x => x.Add(p)).Throws(new Exception("Shouldn't be called."));
// act
var actual = sut.Create(p);
// assert
Assert.IsInstanceOfType(actual, typeof(ViewResult));
}
[TestMethod]
public void PersonsController_Create_Action_Call_Repository()
{
// arrange
var fakeRepo = new Mock<IPersonRepository>();
var sut = new PersonController(fakeRepo.Object);
var p = new Person();
fakeRepo.Setup(x => x.Add(p)).Returns(5).Verifiable();
// act
var actual = sut.Create(p);
// assert
Assert.IsInstanceOfType(actual, typeof(JsonResult));
var jsonResult = (JsonResult)actual;
var data = new RouteValueDictionary(jsonResult.Data);
Assert.AreEqual(5, data["id"]);
fakeRepo.Verify();
}
You need to make your DBContext injectable by extracting an interface for it:
public interface IDBContext{
IList<Person> People {get;} // I'm guessing at the types
void SaveChanges();
// etc.
}
Then inject that into your concrete class:
public class PersonRepository : IPersonRepository
{
IDBContext ctx;
public PersonRepository(IDBContext db) {
ctx = db;
}
public int Add(Person person)
{
// New entity
ctx.People.Add(person);
ctx.SaveChanges();
return person.id;
}
}
Your test would then look like:
[Test]
public void Creating_A_Person_Should_Return_The_ID ()
{
Mock<IDBContext> mockDbContext = new Mock<IDBContext>();
// Setup whatever mock values/callbacks you need
PersonRepository repository = new PersonRepository(mockDbContext.Object);
int id = repository.Add(new Person { Name = "Some Name" });
Assert.IsNotNull(id);
// verify that expected calls are made against your mock
mockDbContext.Verify( db => db.SaveChanges(), Times.Once());
//...
}
I'd personally look at writing an "integration test" for this, i.e. one that hits a real (ish) database as your data access layer should not contain any logic which makes testing in isolation worthwhile.
In this case you will require a database up and running. This could be a developer database already set up somewhere, or an in-memory database started as part of the tests arrange.
The reason for this is that I find (pure) unit tests of the DAL generally end up as proof you can use a mock framework and little more and don't end up giving you much confidence in your code.
If you are completely new to unit tests and don't have colleges on hand to help set up the environment required for DAL testing you then I'd recommend you leave testing the DAL for now and concentrate on the business logic as this is where you will get the biggest bang for your buck and will make it easier see how the tests will help you.