unit testing a Delete controller method - asp.net-mvc-3

I am trying to test the delete method from my controller.
I have the following test case:
[TestMethod()]
[DeploymentItem("Courses.sdf")]
public void RemoveCourseConfirmedTest()
{
CoursesController target = new CoursesController();
int id = 50;
ActionResult actual;
CoursesDBContext db = new CoursesDBContext();
Course courseToDelete = db.Courses.Find(id);
List<CourseMeet> meets = db.Meets.Where(a => a.courseID == id).ToList();
actual = target.RemoveCourseConfirmed(courseToDelete);
foreach (CourseMeet meet in meets)
{
Assert.IsFalse(db.Meets.Contains(meet));
}
Assert.IsFalse(db.Courses.Contains(courseToDelete));
}
This is the controller method
[HttpPost, ActionName("RemoveCourse")]
public ActionResult RemoveCourseConfirmed(Course course)
{
try
{
db.Entry(course).State = EntityState.Deleted;
db.SaveChanges();
return RedirectToAction("Index");
}
catch (DbUpdateConcurrencyException)
{
return RedirectToAction("RemoveMeet", new System.Web.Routing.RouteValueDictionary { { "concurrencyError", true } });
}
catch (DataException)
{
ModelState.AddModelError(string.Empty, "Unable to save changes. Try again.");
return View(course);
}
}
However when I run the test case I get the following exception.
System.InvalidOperationException: An entity object cannot be referenced by multiple instances of IEntityChangeTracker.
I ran the test through the debugger and found that the problem in on the following line:
db.Entry(course).State = EntityState.Deleted;
I am not sure why this is happening.

The biggest problem I see here is that you are testing directly against the database. Your unit tests should mock out the database dependencies especially.
However, if you are deadset on doing it this way, then the error you are getting is coming from the fact that your test is opening a CoursesDBContext, and then your code under test appears to be using its own CoursesDBContext. So, the error is because you cannot try to reference the same entity from different contexts. You will need some way of passing in your context to your class (either a public property or being passed through the parameters)

Related

how to unit test controller when automapper is used?

here's my controller
[POST("signup")]
public virtual ActionResult Signup(UserRegisterViewModel user)
{
if (ModelState.IsValid)
{
var newUser = Mapper.Map<UserRegisterViewModel, User>(user);
var confirmation = _userService.AddUser(newUser);
if (confirmation.WasSuccessful)
return RedirectToAction(MVC.Home.Index());
else
ModelState.AddModelError("Email", confirmation.Message);
}
return View(user);
}
here's my unit test:
[Test]
public void Signup_Action_When_The_User_Model_Is_Valid_Returns_RedirectToRouteResult()
{
// Arrange
const string expectedRouteName = "~/Views/Home/Index.cshtml";
var registeredUser = new UserRegisterViewModel { Email = "newuser#test.com", Password = "123456789".Hash()};
var confirmation = new ActionConfirmation<User>
{
WasSuccessful = true,
Message = "",
Value = new User()
};
_userService.Setup(r => r.AddUser(new User())).Returns(confirmation);
_accountController = new AccountController(_userService.Object);
// Act
var result = _accountController.Signup(registeredUser) as RedirectToRouteResult;
// Assert
Assert.IsNotNull(result, "Should have returned a RedirectToRouteResult");
Assert.AreEqual(expectedRouteName, result.RouteName, "Route name should be {0}", expectedRouteName);
}
Unit test failed right here.
var result = _accountController.Signup(registeredUser) as RedirectToRouteResult;
when I debug my unit test, I got following error message: "Missing type map configuration or unsupported mapping."
I think its because configuration is in web project, not the unit test project. what should I do to fix it?
You need to have the mapper configured, so in your test class set up, not the per-test setup, call the code to set up the mappings. Note, you'll also probably need to modify your expectation for the user service call as the arguments won't match, i.e, they are different objects. Probably you want a test that checks if the properties of the object match those of the model being passed to the method.
You should really use an interface for the mapping engine so that you can mock it rather than using AutoMapper otherwise it is an integration test not a unit test.
AutoMapper has an interface called IMappingEngine that you can inject into your controller using your IoC container like below (this example is using StructureMap).
class MyRegistry : Registry
{
public MyRegistry()
{
For<IMyRepository>().Use<MyRepository>();
For<ILogger>().Use<Logger>();
Mapper.AddProfile(new AutoMapperProfile());
For<IMappingEngine>().Use(() => Mapper.Engine);
}
}
You will then be able to use dependency injection to inject AutoMapper's mapping engine into your controller, allowing you to reference your mappings like below:
[POST("signup")]
public virtual ActionResult Signup(UserRegisterViewModel user)
{
if (ModelState.IsValid)
{
var newUser = this.mappingEngine.Map<UserRegisterViewModel, User>(user);
var confirmation = _userService.AddUser(newUser);
if (confirmation.WasSuccessful)
return RedirectToAction(MVC.Home.Index());
else
ModelState.AddModelError("Email", confirmation.Message);
}
return View(user);
}
You can read more about this here: How to inject AutoMapper IMappingEngine with StructureMap
Probably it is cool to abstract mapping into MappingEngine.
Sometimes I use following approach to IOC Automapper
In IOC builder:
builder.RegisterInstance(AutoMapperConfiguration.GetAutoMapper()).As<IMapper>();
where GetAutoMapper is:
public class AutoMapperConfiguration
{
public static IMapper GetAutoMapper()
{
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile<OrderModelMapperProfile>();
cfg.AddProfile<OtherModelMapperProfile>();
//etc;
});
var mapper = config.CreateMapper();
return mapper;
}
}
And finally in Controller ctor
public MyController(IMapper mapper)
{
_mapper = mapper;
}

"The ObjectContext instance has been disposed" - even with a using(context) statement and ToList()

I have an MVC3 Project running with EF Code First.
Here is my code for Home/Index:
public ActionResult Index()
{
var IndVM = new IndexVM();
using (QuoteContext QDomain = new QuoteContext())
{
IndVM.Quotes = QDomain.Quotes.Include("Tags").Include("Author").OrderByDescending(x => x.CreatedOn).Take(5).ToList();
IndVM.Tags = QDomain.Tags.OrderByDescending(x => x.Quotes.Count).ToList();
IndVM.Authors = QDomain.Authors.OrderByDescending(x => x.Quotes.Count).Take(5).ToList();
}
return View(IndVM);
}
As you can see I have the Querying stuff inside a using statement, and I am also calling the ToList(), but I still get the error:
The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
Is this a bug in EF Code First?
You have to turn off lazy loading, otherwise the serializer will try to traverse navigation properties and throw this exception.
public ActionResult Index()
{
var IndVM = new IndexVM();
using (QuoteContext QDomain = new QuoteContext())
{
QDomain.ContextOptions.LazyLoadingEnabled = false;
// Query and populate IndVM here...
}
return View(IndVM);
}

RedirectToAction not working as expected

I have a simple MVC3 application that I want to retrieve some configuration details from a service, allow the user to edit and save the configuration.
If any errors are detected during the saving process, these are to be returned and reported back to the user.
The problem is that the configuration containing the errors is failing to be called and the currently saved values are just being redisplayed.
Stepping through the code, when errors are detected, it should redirect to itself using the passed config object but it doesn't and uses the method with no parameter.
Can anyone see where I'm going wrong?
Below are the two controller methods that are being called:
//
// GET: /Settings/Edit/
public ActionResult Edit()
{
SettingsViewModel config = null;
// Set up a channel factory to use the webHTTPBinding
using (WebChannelFactory<IChangeService> serviceChannel =
new WebChannelFactory<IChangeService>(new Uri(baseServiceUrl)))
{
// Retrieve the current configuration from the service for editing
IChangeService channel = serviceChannel.CreateChannel();
config = channel.GetSysConfig();
}
ViewBag.Message = "Service Configuration";
return View(config);
}
//
// POST: /Settings/Edit/
[HttpPost]
public ActionResult Edit( SettingsViewModel config)
{
try
{
if (ModelState.IsValid)
{
// Set up a channel factory to use the webHTTPBinding
using (WebChannelFactory<IChangeService> serviceChannel = new WebChannelFactory<IChangeService>(new Uri(baseServiceUrl)))
{
IChangeService channel = serviceChannel.CreateChannel();
config = channel.SetSysConfig(config);
// Check for any errors returned by the service
if (config.ConfigErrors != null && config.ConfigErrors.Count > 0)
{
// Force the redisplay of the page displaying the errors at the top
return RedirectToAction("Edit", config);
}
}
}
return RedirectToAction("Index", config);
}
catch
{
return View();
}
}
return RedirectToAction("Index", config);
You cannot pass complex objects like this when redirecting. You will need to pass query string parameters one by one:
return RedirectToAction("Index", new {
Prop1 = config.Prop1,
Prop2 = config.Prop2,
...
});
Also I couldn't see an Index action in your controller. Maybe it's a typo. Another thing I notice is that you have an Edit GET action to which you are probably trying to redirect but this Edit action doesn't take any parameters so it just seems weird. If you are trying to redirect to the POST Edit action, well, that's obviously impossible since a redirect is always on GET by its very nature.

MVC3 binding nested model using Entity Framework with GUID keys

I have been fighting with this all day and still I am failing.
I can simplify the problem as follows:
I have reports and reports have forms. I have entity models of each. They have Guid id's as shown below.
I am trying to get a single view where I can create a report and a form. As an end goal I would like to be able to add multiple forms, but just one would be great. My controller is as follows:
// GET: /AllInOne/Create
public ActionResult Create()
{
ViewBag.PossibleReportBases = reportBaseRepository.All;
ViewBag.PossibleCategories = categoryRepository.All;
var model = new Report {FromDate = DateTime.Now};
model.Forms.Add(new Form());
return View(model);
}
// POST: /AllInOne/Create
[HttpPost]
public ActionResult Create(Report report)
{
if (ModelState.IsValid) {
reportRepository.InsertOrUpdate(report);
reportRepository.Save();
return RedirectToAction("Index");
}
else
{
ViewBag.PossibleReportBases = reportBaseRepository.All;
ViewBag.PossibleCategories = categoryRepository.All;
return View();
}
}
The repository code looks like this:
public void InsertOrUpdate(Report report)
{
if (report.Id == default(System.Guid)) {
// New entity
report.Id = Guid.NewGuid();
context.Reports.AddObject(report);
} else {
// Existing entity
context.Reports.Attach(report);
context.ObjectStateManager.ChangeObjectState(report, EntityState.Modified);
}
}
At one stage the binding was giving me this error:
The EntityCollection has already been initialized. The InitializeRelatedCollection method should only be called to initialize a new EntityCollection during deserialization of an object graph.
I have tried many things for the views, but none of them have worked.
Please help.
i dont' think you need to bother with the attaching. if you've selected the report from your context, it is already being tracked. you can simplify your repository like so
public void InsertOrUpdate(Report report)
{
// i prefer Guid.Empty but no big deal
if (report.Id == default(System.Guid)) {
// New entity
report.Id = Guid.NewGuid();
context.Reports.AddObject(report);
}
context.SaveChanges();
}

asp.net mvc ObjectDisposedException with ef

I need some help with this error "The ObjectContext instance has been disposed and can no longer be used for operations that require a connection."
It's a asp.net mvc3, EF4, and ms sql.
Here is the razor with two dropdowns:
<div class="editRow">
#Html.DropDownListFor(m=>m.IndustryId, (SelectList)ViewBag.Industry, #Empower.Resource.General.ddlDefaultVal, new { #class = "ddl400" })
#Html.ValidationMessageFor(m => m.IndustryId)
</div>
<div class="editRow">
#Html.DropDownListFor(m=>m.ProvinceId, (SelectList)ViewBag.Province, #Empower.Resource.General.ddlDefaultVal, new {#class = "ddl400"})
#Html.ValidationMessageFor(m => m.ProvinceId)
</div>
Controller:
IndustryService indService = new IndustryService();
ViewBag.Industry = new SelectList(indService.GetAllIndustry(), "IndustryId", "IndustryName");
ProvinceService proService = new ProvinceService();
ViewBag.Province = new SelectList(proService.GetAllProvince(), "ProvinceId", "ProvinceName");
return View();
ProvinceService:
public IEnumerable<Province> GetAllProvince()
{
using (var context = DBContext.ObjectContext)
{
var pros = context.Provinces;
return pros;
}
}
IndustryService is identical as above...
public class DBContext
{
private static EmpowerDBEntities _empowerContext;
public static EmpowerDBEntities ObjectContext
{
get
{
if (_empowerContext == null)
_empowerContext = new EmpowerDBEntities();
return _empowerContext;
}
}
}
I know the problem occurs in second dropdown when it tries to retrive data while the connection is desposed by previous query. Please help me with this, thanks.
The fix is simple - convert to a .ToList() or First() before using. LINQ has deferred execution and tries to run this command after the context is disposed (when your object results are referenced) - not when you actually make the call.. You need to force it to run now while the context is in scope.
using (var context = DBContext.ObjectContext)
{
var pros = context.Provinces;
return pros.ToList();
}
Also - your code above is checking for null in the get accessor. However this object won't be null - it will be disposed, so you cannot do your check this way, you need to check if its null and not disposed.
public class DBContext
{
private static EmpowerDBEntities _empowerContext;
public static EmpowerDBEntities ObjectContext
{
get
{
if (_empowerContext == null || _empowerContext.IsDisposed())
_empowerContext = new EmpowerDBEntities();
return _empowerContext;
}
}
}
something like that anyways :)
I ran into a similar problem. I had been following this pattern, which I had seen in many code examples on the web:
public ActionResult Show(int id)
{
using (var db = new MyDbContext())
{
var thing = db.Things.Find(id);
return View(thing);
}
}
However, this was causing the ObjectDisposedException listed above whenever I tried to access anything that hadn't been loaded into memory in the View code (in particular, one-to-many relationships in the main view model).
I found a different pattern in this example:
public class MyController : Controller
{
private MyDbContext _db;
public MyController()
{
_db = new MyDbContext();
}
public ActionResult Show(int id)
{
// Do work such as...
var thing = _db.Things.Find(id);
return View(thing);
}
protected override void Dispose(bool disposing)
{
_db.Dispose();
base.Dispose(disposing);
}
}
This pattern keeps the database connection alive until the View has finished rendering, and neatly disposes of it when the Controller itself is disposed.
Here is the problem when you are using
using(var context=new CustomerContext())
{
return View(context.Customers.ToList());
}
when the block of code executes all references are disposed that you are lazzy loading so that's why it is throwing this error.
so i used
return View(context.Customers.ToList()) directly it will work perfectly fine.

Resources