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.
Related
Just wanted some guidance how would I go about unit testing the following action:
public ActionResult Index()
{
var model = _resolver.GetService<ISignUpViewModel>();
model.Location = _resolver.GetService<ILocations>().getLocations(string area);
return PartialView("Login", model);
}
private IDependencyResolver _resolverMock;
[TestMethod]
public void SignUpTest()
{
var _resolverMock = new Mock<IDependencyResolver>();
var ctrl = new HomeController(_resolverMock.Object);
var signUpMock = new Mock<ISignUpViewModel>();
var LocationsMock = new Mock<ILocations>();
_resolverMock.Setup(m => m.GetService(It.IsAny<Type>())).Returns(
signUpMock.Object);
_resolverMock.Setup(m => m.GetService(It.IsAny<Type>())).Returns(
LocationsMock.Object);
DependencyResolver.SetResolver(resolverMock.Object);
ctrl.Index();
ctrl.ViewData.Model = signUpMock;
}
How do you build up the model in the unit test?
Also how do I call the getLocations method from the resolver?
Unsure on how to do this?
Your home controller is not dependent on an IDependencyResolver as per your code. It is in fact dependent on an ISignUpViewModel. So you can either pass one of those into the constructor or an ISignUpViewModelFactory. So I would refactor the model resolution out into a factory class:
public class HomeController {
private readonly ISignUpViewModelFactory _modelFactory;
public HomeController(ISignUpViewModelFactory modelFactory){
_modelFactory = modelFactory;
}
public ActionResult Index()
{
return PartialView("Login", _modelFactory.Create(area));
}
}
public interface ISignUpViewModelFactory {
ISignUpViewModel Create(string area);
}
public class ProductionSignUpViewModelFactory : ISignUpViewModelFactory
{
public ISignUpViewModel Create(string area){
// create and return your models here
// if you still have to use a service locator in this factory then
// refactor some more until you get these dependencies out in the open.
}
}
public class MockSignUpViewModelFactory : ISignUpViewModelFactory
{
public ISignUpViewModel Create(string area){
return new SignUpViewModel();
}
}
In production you have your IOC inject an instance of ProductionSignUpViewModelFactory. In test, you pass in a MockSignUpViewModelFactory.
The difference is that using this method you are only testing the action (i.e. the unit) whereas using your current method you are testing the action AND your servicelocator _resolver plus you've obscured the actual dependency i.e. ISignUpViewModel not IDependencyResolver.
As per the comments in ProductionSignUpViewModelFactory, if you have to inject an IDependencyResolver into that factory to make it work then you're probably still doing it wrong. You then need to look at what ProductionSignUpViewModelFactory needs to create an instance of ISignUpViewModel and inject that not an IDependencyResolver.
Eventually you'll get to the top of your dependency chain and you'll find unit testing becomes very, very easy. It's even easier if you build the tests first i.e. TDD. :)
I've never used any Mock frameworks and actually new to ASP.NET MVC, testing and all this related stuff.
I'm trying to figure out how to use Moq framework for testing, but can't make it work. that's what I have at the moment: My repository interface:
public interface IUserRepository {
string GetUserEmail();
bool UserIsLoggedIn();
ViewModels.User CurrentUser();
void SaveUserToDb(ViewModels.RegisterUser viewUser);
bool LogOff();
bool LogOn(LogOnModel model);
bool ChangePassword(ChangePasswordModel model);
}
My Controller constuctor, I'm using Ninject for injection, it works fine
private readonly IUserRepository _userRepository;
public HomeController(IUserRepository userRepository) {
_userRepository = userRepository;
}
Simplest method in controller:
public ActionResult Index() {
ViewBag.UserEmail = _userRepository.GetUserEmail();
return View();
}
And my test method:
[TestMethod]
public void Index_Action_Test() {
// Arrange
string email = "test#test.com";
var rep = new Mock<IUserRepository>();
rep.Setup(r => r.GetUserEmail()).Returns(email);
var controller = new HomeController(rep.Object);
// Act
string result = controller.ViewBag.UserEmail;
// Assert
Assert.AreEqual(email, result);
}
I assume that this test must pass, but it fails with message Assert.AreEqual failed. Expected:<test#test.com>. Actual:<(null)>.
What am I doing wrong?
Thanks
Simple - you do not do Act part correctly. Fisrt you should call Index() action of the controller, and then Assert ViewBag.UserEmail correctness
// Act
controller.Index();
string result = controller.ViewBag.UserEmail;
By the way, advice - Using ViewBag is not the good practice. Define ViewModels instead
I am trying to get my feet wet with asp.net mvc3 and unit testing.
I have created a model which uses the repository pattern. Here's the interface:
public interface IExtensionRepository
{
IList<Extensions> ListAll();
}
Here's the repository:
public class ExtensionRepository : IExtensionRepository
{
private ExtensionsLSDataContext _dataContext;
public ExtensionRepository()
{
_dataContext = new ExtensionsLSDataContext();
}
public IList<Extensions> ListAll()
{
var extensions = from ext in _dataContext.Extensions
select ext;
return extensions.ToList();
}
}
Here's the controller:
public class ExtensionController : Controller
{
private IExtensionRepository _repository;
public ExtensionController()
: this(new ExtensionRepository())
{
}
public ExtensionController(IExtensionRepository repository)
{
_repository = repository;
}
}
The pages seem to function as designed. Things go astray with my unit test, however. It resides in another project in the same solution. I am using Moq and NUnit. Here's my test:
[Test]
public void Test_Extension_Index_Views()
{
Mock<Extensions> extension = new Mock<Extensions>();
List<Extensions> extList = new List<Extensions>();
extension.Object.Extension = "5307";
extension.Object.Extension_ID = 1;
extension.Object.ExtensionGroup_ID = 1;
extList.Add(extension.Object);
Mock<IExtensionRepository> repos = new Mock<IExtensionRepository>();
repos.Setup(er => er.ListAll()).Returns(extList);
var controller = new ExtensionController(repos);
var result = controller.Index() as ViewResult;
Assert.AreEqual("Index", result.ViewName);
}
I am getting the following errors for the line that begins "var controller...":
The best overloaded method match for
'MvcApplication1.Controllers.ExtensionController.ExtensionController(MvcApplication1.Models.IExtensionRepository)'
has some invalid arguments
And:
Argument 1: cannot convert from
'Moq.Mock'
to
'MvcApplication1.Models.IExtensionRepository'
I know I've missed the boat somewhere, but I haven't a clue as to where... any ideas?
Change this:
var controller = new ExtensionController(repos);
to this:
var controller = new ExtensionController(repos.Object);
PS.: I know it sucks, but that's the way Moq was designed.
I'm using ASP.NET MVC 2, NUnit, Moq and MVC Contrib. I have written my first unit test ever, and I have a couple of questions regarding this test. My scenario is that I have an Index view. On this view I have a grid that displays all the news items.
Here is my INewsRepository class:
public interface INewsRepository
{
IEnumerable<News> FindAll();
}
My test class with the test method:
public class NewsControllerTest :TestControllerBuilder
{
private Mock<INewsRepository> mockNewsRepository;
private NewsController newsController;
[SetUp]
public void Init()
{
mockNewsRepository = new Mock<INewsRepository>();
newsController = new NewsController(mockNewsRepository.Object);
InitializeController(newsController);
}
[Test]
public void NewsController_Index()
{
// Arrange
var news = new Mock<IEnumerable<News>>();
mockNewsRepository.Setup(r => r.FindAll()).Returns(news.Object).Verifiable();
// Act
ActionResult actual = newsController.Index();
// Assert
mockNewsRepository.Verify();
actual
.AssertViewRendered()
.ForView("Index")
.WithViewData<News[]>()
.ShouldBe(news);
}
}
My view:
public ActionResult Index()
{
FakeNewsRepository fakeNewsRepository = new FakeNewsRepository();
IEnumerable<News> news = fakeNewsRepository.FindAll();
return View(news);
}
I need some pointers on the way that I did it. Am I in the correct direction? What should I add, what should I leave out? I want to do unit testing, am I mixing it with integration testing? Any extra advice would be appreciated.
When I run this test in the NUnit GUI console then I get an error back and I'mnot sure what it means:
MyProject.Web.Tests.Controllers.NewsControllerTest.NewsController_Index:
Moq.MockVerificationException : The following setups were not matched:
INewsRepository r => r.FindAll()
public ActionResult Index()
{
FakeNewsRepository fakeNewsRepository = new FakeNewsRepository();
IEnumerable<News> news = fakeNewsRepository.FindAll();
return View(news);
}
You cannot mock the repository that is hardcoded like this in your action. You are instantiating it inside the action, you will never be able to unit test this and mock the repository. The repository needs to be injected as a dependency. You could use an interface and pass this interface to the constructor of your controller:
public class HomeController: Controller
{
private readonly IRepository _repository;
public class HomeController(IRepository repository)
{
_repository = repository;
}
public ActionResult Index()
{
IEnumerable<News> news = _repository.FindAll();
return View(news);
}
}
Now in your unit test you could pass the mocked instance of your repository to the controller constructor and define expectations.
Also notice that MVCContrib.TestHelper is designed to work with Rhino Mocks. I am not quite sure whether it works fine with Moq.
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.