I am following an article - http://www.asp.net/web-api/overview/testing-and-debugging/mocking-entity-framework-when-unit-testing-aspnet-web-api-2 - to unit test Web API 2 controller.
There, the author tests put method like below:
//....
Product GetDemoProduct()
{
return new Product() { Id = 3, Name = "Demo name", Price = 5 };
}
[TestMethod]
public void PutProduct_ShouldReturnStatusCode()
{
var controller = new ProductController(new TestStoreAppContext());
//** Edited myself from original:
//** var item = GetDemoProduct();
var updatedItem = new Product(){ Id = 3, Name = "Demo name", Price = 6 };
var result = controller.PutProduct(3, updatedItem) as StatusCodeResult;
Assert.IsNotNull(result);
Assert.IsInstanceOfType(result, typeof(StatusCodeResult));
Assert.AreEqual(HttpStatusCode.NoContent, result.StatusCode);
//** Added a new assertion.
Assert.AreEqual(updatedItem.Price, GetDemoProduct().Price);
}
Which tests put method within controller
public IHttpActionResult PutProduct(int id, Product product)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != product.Id)
{
return BadRequest();
}
//db.Entry(product).State = EntityState.Modified;
db.MarkAsModified(product);
try
{
db.SaveChanges();
}
//....
return StatusCode(HttpStatusCode.NoContent);
}
To verify that update actually happens, I added few lines to the original test method which are indicated by ** in the block.
Well, the mock data item doesn't get updated in the end. Price remains the same as the pre-update product price.
How come db.SaveChanges(); doesn't work?
Just Found out that Author intentionally changed saveChanges method within test context.
public class TestStoreAppContext : IStoreAppContext
{
public TestStoreAppContext()
{
this.Products = new TestProductDbSet();
}
public DbSet<Product> Products { get; set; }
public int SaveChanges()
{
return 0;
}
//....
Now need to think why he changed the saveChanges() method.
Thank you all.
1) You initiallize updatedItem with a Price of 6 (your reference mock has price of 5), pass it to the PutProduct method, but the method never modifies the price. I am not sure what you were expecting to happen. If you don't modify the price, obviously it will be the same. Did you intend to do product.Price = 5 somewhere? Otherwise it will not match your reference object and will still have a Price of 6.
2) Using an assert in the input parameter of a WebAPI method is a bad test. updatedItem is going to in practice come from an HTTP request that is bound to the parameter. Whether you modify the properties of that entity makes little difference to anything outside of the method because nothing else has a reference to it. You have a reference to it in the test case only because that's how we are faking the HTTP request. We skipped the whole model binding step. In reality no one would ever have a reference to it and it would be completely irrelevant after the call completes. You should only be concerned with two things, the state of the database and the return. You've got the return testing down, you just are offbase with testing the database modification. First to emphasize why this test is bad:
Consider if I forgot to call SaveChanges:
public IHttpActionResult PutProduct(int id, Product product)
{
product.Price = 5;
return StatusCode(HttpStatusCode.NoContent);
}
Your assertion would succeed because my modification would cause updatedProduct to have a price of 5 and thus match the reference object:
Assert.AreEqual(updatedItem.Price, GetDemoProduct().Price);
However the implementation of PutProduct is wrong as the programmer forgot to call SaveChanges, but your test case missed this. You would really need to query the database through the same context to see if the changes were actually persisted. You could do this with an actual localdb, which works well if you configure EF to clear DB with each test run, or the more "proper" approach is to use a context that maps to an inmemory mock. This is the approach used here: http://www.c-sharpcorner.com/uploadfile/raj1979/unit-testing-in-mvc-4-using-entity-framework/
If we assumed you had such a repository it'd be something like this:
[TestMethod]
public void Create_PutProductInRepository()
{
int testId = 3;
...
var result = controller.PutProduct(testId, inputProduct) as StatusCodeResult;
...
// This is the kind of testing you really want to be doing.
// You want to "assert that the item exists in the database and has the expected value"
Product productAfterTest = mockrepository.GetProduct(testId);
Assert.AreEqual(inputProduct.Price, productAfterTest.Price);
}
Obivously you are using an approach which does not have a repository, so you'll have to adapt this. Point being, the assertion needs to performa query to retrieve the item from your dbcontext to verify it exists and expected modifications were saved. I'm no expert on making in memory databases, so whatever source you have for your approach you'll have to gain an understanding of. But either way you should be doing your assertions by going to database context and retrieving the item to verify it REALLY was persisted to the context.
Related
I have this method which selects one row of a table and copies it to the another table and then it just deletes it from the first one. The problem is that how can I unit test it because it executes two methods (Post and Delete) at the same time.
Here is the method:
public ActionResult Pay(int id, Car car)
{
try
{
using (BookCarDBEntities db = new BookCarDBEntities())
{
var carToDelete = db.Cars.FirstOrDefault(c => c.Id == id);
var book = CreateNewBooking(carToDelete);
db.Bookings.Add(book);
db.Cars.Remove(carToDelete);
db.SaveChanges();
return RedirectToAction("ListBookings", "Home");
}
}
catch (Exception ex)
{
return View(ex + "error");
}
}
private Booking CreateNewBooking(Car car)
{
var bookingCreated = new Booking
{
id = car.Id,
model = car.model,
make = car.make,
price = car.price,
location = car.location
};
return bookingCreated;
}
It does not matter how many external function calls your method to have. To get the values from the method calls in your method (that you wanna unit test), you should mock those methods to return certain responses when called with certain parameters.
You should check Moq to learn how to mock methods. Also, I suggest you wrap your methods with an interface (BookCarDBEntities). It is more important to write code to testable rather than writing tests to unmanageable code. Hope this helps.
Below is my unit test method(c#) :-
[TestMethod]
public void ShouldReturnDtosWhenProductsFound_GetProducts()
{
// Arrrange
var count = 0;
var name = "myproduct";
var description = "desc";
// setup mocked dal to return list of products
// when name and description passed to GetProducts method
_productDalMock.Setup(d => d.GetProducts(name, description)).Returns(_productList);
// Act
List<ProductDto> actual = _productService.GetProducts(name, description);
// Assert
Assert.IsNotNull(actual);
Assert.IsTrue(actual.Any());
Assert.AreEqual(_productList.Count, actual.Count);
foreach (var product in _productList)
{
Adapter.AssertAreEqual(product, actual[count]);
count++;
}
// verify all setups of mocked dal were called by service
_productDalMock.VerifyAll();
}
I am using Mock object to mocking the GetProducts method(dependency).And in mocking i am returning the already declared product list(_productList).
My problem is when i am debugging the test,i am not getting the same product list in actual object as i passed in mocking.And according to my exploration we will get the same list of objects in actual result which we pass in mock object.
Can any one suggest me what is going wrong here ?
Edit
I just want to know its necessary for mock object to return the same values with actual object or it can be change ?
When you initialize _productService, inject the _productDalMock you're setting up. This is typically done in the constructor of _productService, after you have initialized the _productDalMock instance.
_productService = new ProductService(_productDalMock);
In your debugging, verify that the _productService.GetProducts code finally hits the _productDal.GetProducts code line. i.e. there is no code before the DAL call which would make the _productService.GetProducts method return etc.
If you ensure the above 2 steps, then the actual products returned should be same as the one you're passing.
I have following method in my mvc controller:
[HttpGet]
public ActionResult UserProfile(String username)
{
var user = db.Users.Find(username);
return View(user);
}
This function returns View with user profile. But result of this is the same, regardless of changes in database.
When I debug it seems like db is not changing at all, while in other controllers everything works just fine.
EDIT:
Place when I make changes
public ActionResult ExecuteRetreive(String username, String ISBN)
{
if (IsValid(username))
{
var resBook = db.Books.Find(ISBN);
var resUser = db.Users.Find(username);
var resRentedBooks = (from rb in db.RentedBooks
join b in db.Books on rb.ISBN equals b.ISBN
where b.ISBN == ISBN
where rb.Login == username
where rb.Returned == null
select rb).FirstOrDefault();
if (resRentedBooks == null)
{
return RedirectToAction("Fail", "FailSuccess",
new { error = "" });
}
resRentedBooks.Returned = DateTime.Now;
resBook.IsRented = false;
resUser.RentedBooks--;
db.SaveChanges();
return RedirectToAction("Success", "FailSuccess");
}
else
{
return RedirectToAction("Fail", "FailSuccess",
new { error = "Niepoprawna nazwa użytkownika" });
}
}
Im new to this so dont laugh at my code :P When I display resUser.RentedBooks--; it is the same every time.
As a follow up to what #JeroenVannevel said in the comments, another problem that you might be having because you're using a static context (and one that I've had to deal with in the past) is that once a specific DbContext has loaded an entity (or a set of entities, in my case) it won't tend to refresh just because some outside changes were made in the database. It loads those entities into Local and just refers to those automatically if you query for it.
The solution, then, is to always put your DbContext calls wrapped up in a using block, since DbContext implements IDisposable.
One word of caution with this approach, since you're using MVC: If you are using lazy loading, and you know that your View will need some information from a child object (or to list the names of a collection of child objects), you will absolutely need to hydrate those child entities before you get out of the using block, or you will find yourself getting exceptions saying that your context has been disposed.
I've read more and more about unit testing, and determined to put it to work. I dug out a project which is written with ASP.NET MVC using the repository pattern, dependency injection and EF. My first task was to unit test a controller. Here is a snippet from the controller to test:
IUserRepository _userRepository;
IAttachmentRepository _attachmentRepository;
IPeopleRepository _peopleRepository;
ICountryRepository _countryRepository;
public UserController(IUserRepository userRepo, IAttachmentRepository attachRepo, IPeopleRepository peopleRepo, ICountryRepository countryRepo)
{
_userRepository = userRepo;
_attachmentRepository = attachRepo;
_peopleRepository = peopleRepo;
_countryRepository = countryRepo;
}
public ActionResult Details()
{
UserDetailsModel model = new UserDetailsModel();
foreach (var doc in _attachmentRepository.GetPersonAttachments(Globals.UserID))
{
DocumentItemModel item = new DocumentItemModel();
item.AttachmentID = doc.ID;
item.DocumentIcon = AttachmentHelper.GetIconFromFileName(doc.StoragePath);
item.DocumentName = doc.DocumentName;
item.UploadedBy = string.Format("{0} {1}", doc.Forename, doc.Surname);
item.Version = doc.VersionID;
model.Documents.Add(item);
}
var person = _peopleRepository.GetPerson();
var address = _peopleRepository.GetAddress();
model.PersonModel.DateOfBirth = person.DateOfBirth;
model.PersonModel.Forename = person.Forename;
model.PersonModel.Surname = person.Surname;
model.PersonModel.Title = person.Title;
model.AddressModel.AddressLine1 = address.AddressLine1;
model.AddressModel.AddressLine2 = address.AddressLine2;
model.AddressModel.City = address.City;
model.AddressModel.County = address.County;
model.AddressModel.Postcode = address.Postcode;
model.AddressModel.Telephone = address.Telephone;
model.DocumentModel.EntityType = 1;
model.DocumentModel.ID = Globals.UserID;
model.DocumentModel.NewFile = true;
var countries = _countryRepository.GetCountries();
model.AddressModel.Countries = countries.ToSelectListItem(1, c => c.ID, c => c.CountryName, c => c.CountryName, c => c.ID.ToString());
return View(model);
}
I want to test the Details method and have the following queries:
1) The Globals.UserID property retrieves the current user from the session object. How can I easily test this (I'm using the built in VS2010 unit testing and Moq)
2) I'm making a call to AttachmentHelper.GetIconFromFileName() here which simply looks at the extension of a file and displays an icon. I'm also making a call to GetPersonAttachments in the attachment repository, a call to GetPerson, GetAddress and GetCountries as well as a call to an extension method created transform a List to IEnumerable of SelectListItem.
Is this controller action an example of bad practice? It's using lots of repositories and also other helper methods. From what I can see, unit testing this single action will require lots and lots of code. Is this counter productive?
Unit testing a simple controller in a test project is one thing, but when you get into real life code such as this it could become a monster.
I guess my question really is should I refactor my code to make it easier to test, or should my tests become much more complex to satisfy the current code?
Complex tests are as bad as complex code: they're prone to bugs. So, in order to keep your tests simple it's generally a good idea to refactor your application code to make it easier to test. For example, you should pull out the mapping code from your Details() method into separate helper methods. You can then test those methods very easily and not have to worry so much about testing all the crazy combinations of Details().
I've pulled out the person and address mapping parts below, but you could pull it apart even more. I just wanted to give you an idea of what I meant.
public ActionResult Details() {
UserDetailsModel model = new UserDetailsModel();
foreach( var doc in _attachmentRepository.GetPersonAttachments( Globals.UserID ) ) {
DocumentItemModel item = new DocumentItemModel();
item.AttachmentID = doc.ID;
item.DocumentIcon = AttachmentHelper.GetIconFromFileName( doc.StoragePath );
item.DocumentName = doc.DocumentName;
item.UploadedBy = string.Format( "{0} {1}", doc.Forename, doc.Surname );
item.Version = doc.VersionID;
model.Documents.Add( item );
}
var person = _peopleRepository.GetPerson();
var address = _peopleRepository.GetAddress();
MapPersonToModel( model, person );
MapAddressToModel( model, address );
model.DocumentModel.EntityType = 1;
model.DocumentModel.ID = Globals.UserID;
model.DocumentModel.NewFile = true;
var countries = _countryRepository.GetCountries();
model.AddressModel.Countries = countries.ToSelectListItem( 1, c => c.ID, c => c.CountryName, c => c.CountryName, c => c.ID.ToString() );
return View( model );
}
public void MapAddressToModel( UserDetailsModel model, Address address ) {
model.AddressModel.AddressLine1 = address.AddressLine1;
model.AddressModel.AddressLine2 = address.AddressLine2;
model.AddressModel.City = address.City;
model.AddressModel.County = address.County;
model.AddressModel.Postcode = address.Postcode;
model.AddressModel.Telephone = address.Telephone;
}
public void MapPersonToModel( UserDetailsModel model, Person person ) {
model.PersonModel.DateOfBirth = person.DateOfBirth;
model.PersonModel.Forename = person.Forename;
model.PersonModel.Surname = person.Surname;
model.PersonModel.Title = person.Title;
}
Just wanted to elaborate a little bit on the subj. What we're trying to unit test is the logic. Not too much of it in the controller. So in this particular case I would do the next: extract method that returns the model not the view. Inject mocked repos into the controller object. And after exercising the mapping would insure that all the properties are filled with expected values. Another way of doing that is to generate JSON and insure that all the properties are filled appropriately. However, I would strive to put unit tests on the mapping part itself and then would consider BDD for the integration tests.
I'd move all of your Model construction code into the constructor of the model itself. I prefer to keep the controllers restricted to a very few simple tasks:
Selecting the proper view (if the controller action allows for more than one view)
Selecting the correct view model
Permissions/security
View model validation
Thus, your Details controller becomes much simpler and testing becomes more more managable:
public ActionResult Details() {
return View(new UserDetailsModel(Globals.UserId);
}
Now that your controller is tight and testable, let's look at your model:
public class UserDetailsModel {
public UserDetailsModel(int userId) {
... instantiation of properties goes here...
}
... public properties/methods ...
}
Again, the code in your model is encapsulated and only needs to worry specifically about it's properties.
As already mentioned by #KevinM1, if you're practicing TDD (you have that tag in your question), you're writing the test before the implementation.
You first write a test for your controller's Detail method. When you write this test you notice that you need to map a person to a UserDetailsModel. When writing test you "hide complexity" that don't belong to the actual implementation of what you want to test behind an abstraction. In this case, you'd probably create an IUserDetailModelMapper. When this first test is written, you make it green by creating the controller.
public class UserController
{
ctor(IUserRepository userRepo, IUserDetailModelMapper mapper){...}
public ActionResult Details()
{
var model = _mapper.Map(_userRepo.GetPerson());
return View(model);
}
}
When you later write test for your mapper, you said you need to use some static prop called Globals.UserId. Generally I would avoid static data if possible, but if this is a legacy system you need to "objectify" this to get i testable. One simple way is to hide it behind an interface, something like this...
interface IGlobalUserId
{
int GetIt();
}
...and do an implementation where you use your static data. From now on, you can inject this interface instead to hide the fact that it's static data.
The same thing goes for "AttachmentHelper". Hide it behind an interface. Generally though, there should be alarm bells for XXXHelpers - I would say it's a sign of not placing method where they should be (part of an object) but rather a mix of all kinds of stuff that been mixed together.
I am developing Moq tests for various entities. I can setup create and delete tests fine, but not update - the entity in the repository does not change. I know this is due to the PersistAll doing nothing (probably due to a setup I am missing).
This is a sample of an insert persist setup (I am looking for an Update version):
agg.Setup(a => a.InsertOnPersist<Thingy>(model)).Callback(() => mockThingies.Add(model));
In addition, I also have this to link the List to being the repository:
agg.Setup(a => a.GetObjectStore<Artist>()).Returns(mockThingies.AsQueryable());
This is a sample of an update test I have:
public List<Thingy> mockThingies; //this is our repository
[TestInitialize]
public void SetupTests()
{
mockThingies= new List<Thingy>();
Thingy someThingy = new Thingy();
someThingy.Name = "MyName";
someThingy.ID = 1;
mockThingies.Add(someThingy);
}
[TestMethod]
public void CanEditExistingThingy()
{
Mock<BusinessExceptionBroadcaster> beb = new Mock<BusinessExceptionBroadcaster>();
Mock<IValidationEngine> valid = new Mock<IValidationEngine>();
Mock<IAggregate> agg = new Mock<IAggregate>();
agg.Setup(a => a.GetObjectStore<Thingy>()).Returns(mockThingies.AsQueryable());
ThingyRepository repo = new ThingyRepository (agg.Object);
ThingyService service = new ThingyService (repo, beb.Object, valid.Object);
Thingy newThingy = new Thingy();
newThingy.ID = 1; //same as old
newThingy.Name = "newname"; //new name
Assert.AreNotEqual(newThingy.Name,mockThingies[0].Name);
Assert.IsTrue(service.Update(newThingy));
Assert.AreEqual(newThingy.Name, mockThingies[0].Name); //FAILS HERE
}
This is the code to update:
public bool Update(Thingy entity)
{
Thingy existingThingy= _Thingy.FirstOrDefault(t=>t.ID == entity.ID);
if (existingThingy != null)
{
_Thingy.PersistAll();
return true;
}
else
{
//unimportant
}
}
return false;
}
Don't worry about testing whether the update call actually updates something. You'll just want to verify that your service calls the appropriate method on the repo to perform the update and persist. Testing the actual update is a little outside the scope of this one test.
As far as I can see, it can't work, because you're setting one Thingy with ID=1 in Setup, and then create other one with same ID in test. Although they share same ID, they are not same, so your changes can't be ever propagated to repository.
In fact, I think that it's a bug in your CUT code, because while you're testing ID match, you don't test that your repository knows something about entity you're updating. To add, I personally think that there's something wrong with your repository design if it allows such things.
If we were talking about EntityFramework I'd say you have to attach your entity to context.