Unit testing, dependency injection and AutoMapper annotations
I've just recently used MVC for the first time to build a CRUD application on a SQL Server database. The Entity Framework and the provided scaffolding utilities made this pretty easy. I particularly like how I can use data annotations in the model to centralize label names, validation, etc. for the data fields. I found MVC to be intuitive, organized and I was able to add custom functionality like change tracking using the Entity Framework without a lot of extra code or frustration.
Then I decided to take it to the next level and incorporate unit testing. Things quickly got a lot more complicated.
I read that unit testing with the actual database is problematic and slow and generally not recommended. It's preferable to swap in mock database calls or use an in-memory database substitute. Makes sense. So, the first thing I did was code a separate repository layer to isolate the database calls. I soon discovered that this, in itself, is not sufficient. In order to easily swap one repository for another at test time without rewriting code, it's necessary to inject the correct repository implementation into the controllers through the constructor using the inversion of control pattern with dependency injection. Since dependency injection relies on interfaces, I had to add interface definitions for all the repository classes, install Ninject to manage the injection and wire things up to inject the right interface implementations. I also implemented a Unit of Work interface to encapsulate the process of updating possibly multiple repositories and committing all the changes once.
I then discovered that when editing existing records in my CRUD application the built-in field mappers can't post back an edited object that's constructed with dependency injection. This next led me to the philosophy of separate, simplified view models for each view that only define the precise field information required by the view and using AutoMapper to map the data model to each view model and back again after post. So, after more learning, installing and wiring, I got this working. Unfortunately, I then discovered that while AutoMapper maps the field values between items, it does not map the data annotations. This means that I have to duplicate all the data annotations between the data model and the 4 or 5 view models for each class for the labeling, field validation, etc. to work. I found a few suggestions to address this on SO, but they didn't seem simple or robust. I know this shouldn't be a showstopper, but it's the first serious obstacle I've encountered that I can't find a reasonable solution for and it's bugging me.
My questions:
Does anyone have a proven method for getting AutoMapper to also map the data annotations? Is there another mapper that works similarly but solves this problem?
All of the above work came about from a simple desire to do some unit testing with a substitute database. My project's code size and complexity grew significantly from the original MVC project and classes auto-created using scaffolding. I know the consensus is that this is the "right" way to organize a project for testing and for future development and refactoring, but is it all really worth it? I could have a much simpler project if my tests just used a copy of the real database. I'd love to hear other people's experiences and perspective.
AFAIK, the data annotations you referring to (such as [Display]
) are for the presentation layer. It doesn't make sense to put them into the database model. You don't have to duplicate them if you put them into the right layer.
There are other attributes that are specifically for the data layer (such as field length and table joins) that don't belong in the presentation layer. Basically, you just need to keep them separate according to function.
I ended up only implementing a repository to separate the actual DB calls from the controllers. This let me test the repository functions independently and make sure I was getting the data back I expected.
I decided all the extra complications and overhead of dependency injection, field mapping and duplication of field annotations outweighed the benefits of testing in this case. Especially for a CRUD application where 98% of the functionality is either database access or data display. There's not a not of business logic to test.
链接地址: http://www.djcxy.com/p/74404.html上一篇: 合同确保未经验证的GUID