Unit testing CRUD operation in visual studio 2012 - c#

I am testing the create class in Visual Studio 2012
My controller class is:
public ActionResult Create()
{
return View();
}
//
// POST: /Member/Create
[HttpPost]
public ActionResult Create(Member member)
{
if (ModelState.IsValid)
{
db.Members.Add(member);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(member);
}
And test class is:
[TestClass]
public class MemberTest
{
[TestMethod]
public void Create(Member mem)
{
mem.MemID = 123;
mem.MemName = "sruthy";
/// dont know what is writing.
}
}
SampleDataContext.cs
public class SampleDataContext:DbContext
{
public DbSet<Member> Members { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
I am stuck in test case please help me.

First - create an abstraction for your data access code (mocking DbContext is not very convenient thing):
public interface IMemberRepository
{
void Add(Member member);
}
and make your controller depend on it
public MemberController(IMemberRepository repository)
{
this.repository = repository;
}
This will allow mock data access code easily. Next - write tests which verify controller behavior (I use NUnit and Moq here):
private MemberController controller;
private Mock<IMemberRepository> repositoryMock;
private Member member;
[SetUp]
public void Setup()
{
repositoryMock = new Mock<IMemberRepository>();
controller = new MemberController(repositoryMock.Object);
member = new Member { MemID = 123, MemName = "sruthy" };
}
[Test]
public void ShouldCreateMemberWhenItIsValid()
{
var result = (RedirectToRouteResult)controller.Create(member);
Assert.That(result.RouteValues["action"], Is.EqualTo("Index"));
repositoryMock.Verify(r => r.Add(member));
}
[Test]
public void ShouldNotCreateMemberWhenItIsNotValid()
{
controller.ModelState.AddModelError("MemName", "Something wrong");
var result = (ViewResult)controller.Create(member);
Assert.That(result.ViewName, Is.Empty);
}
And write implementation:
[HttpPost]
public ActionResult Create(Member member)
{
if (ModelState.IsValid)
{
repository.Add(member);
return RedirectToAction("Index");
}
return View(member);
}

What I understood in unit testing is : "test only what your method is doing" So I think you have to test your method is doing well:
ModelState.IsValid
db.Members.Add(member)
db.SaveChanges()
But not the good behavior of ModelState or DbContext. These are tested in their own unit tests. You have to assert only the call is done.
To perform this kind of test you have to use the Dependency injection pattern and replace the real DbContext by mocks. These mocks are just asserting the call is well executed without involving the real dbContext.
I'm not a specialist in unit testing but I think you have to think all your architecture in order to decouple your objects. This allow you to replace real objects by mocks.

Related

Moq: setup a generic method with mocked parameters

I've been trying to write a few tests in NUnit for my generic methods, without success. I hope I can make my situation clear, since I had to heavily paraphrase my code.
DoBusinessLogic() is the method I want to test. It calls two other methods from the base class:
public class MySvc : BaseSvc, IMySvc
{
private readonly IMyRepo _repo;
private readonly IMyConnectorClass _connector
public MySvc(IMyRepo repo) : base(repo)
{
_repo = repo;
_connector = _repo.GetConnector();
}
public async Task DoBusinessLogic(int id1, int id2){
bool doesFirstObjectExist = await CheckMainObjExists<Foo>(id1, _connector);
await CheckSubObjExists<Bar>(id2, _connector);
// further business logic
}
}
Those methods, in turn, call the same repository method, but have different logic after it:
public abstract class BaseSvc : IBaseSvc
{
private readonly IBaseRepo _repo
protected BaseSvc(IBaseRepo repo)
{
_repo = repo;
}
protected async Task<bool> CheckMainObjExists<T>(int? id, IMyConnectorClass connector)
{
return await _repo.GetObjectByIdAsync<T>(Id, connector) != null;
}
protected async Task CheckSubObjExists<T>(int? id, IMyConnectorClass connector)
{
if (await _repo.GetObjectByIdAsync<T>(Id, connector) == null)
{ throw new Exception("Object not found!"); }
}
}
Next, I want to write unit a test for DoBusinessLogic() in the MySvc class. Unfortunately, it seems I can't mock the responses from the repository.
[TestFixture]
public class MySvcTests
{
private MySvc _svc;
private Mock<IMyRepo> _repoMock;
private Mock<IMyConnectorClass> _connectorMock;
[SetUp]
public void SetUp()
{
_repoMock = new Mock<IMyRepo>() {};
_connectorMock = new Mock<IMyConnectorClass>();
_repo.SetUp(r => r.GetConnector()).Return(_connectorMock.Object);
_svc = new MySvc(_repoMock);
}
/*
My intent in this test, is to make CheckMainObjExists() pass,
but make CheckSubObjExist() fail.
*/
[Test]
public async Task DoBusinessLogic_If2ndObjectNotExist_ThrowException()
{
// This should return an object
_repoMock.Setup(r => r.GetObjectByIdAsync<Foo>(It.IsAny<int>(), _connectorMock.Object))
.ReturnsAsync(new Foo());
// This should return null
_repoMock.Setup(r => r.GetObjectByIdAsync<Bar>(It.IsAny<int>(), _connectorMock.Object))
.ReturnsAsync((Bar) null);
Assert.Throws<Exception>(await _svc.DoBusinessLogic());
}
}
However, when I run the test, both methods that I set up for my mock repo return null, whereas I expect a "true" from the first.
I do not know where the problem is situated, but I have my suspicions:
Is it possible to setup a method, using a mocked object as a parameter? In this case, is it possible to use _connectorMock.Object as a setup parameter?
Is it possible to setup the same generic method multiple times, but for a different type each time? It's first setup for Foo, then for Bar.
I just tested this code and it runs as expected. Now I had to make a lot of assumptions just to get the code to compile and run which would mean that my test of your code is flawed as I may have fixed something that was omitted in your example.
I made no changes to your test setup code, which worked.
[TestClass]
public class MySvcTests {
[TestMethod]
[ExpectedException(typeof(Exception))]
public async Task DoBusinessLogic_If2ndObjectNotExist_ThrowException() {
var _repoMock = new Mock<IMyRepo>() { };
var _connectorMock = new Mock<IMyConnectorClass>();
_repoMock.Setup(r => r.GetConnector()).Returns(_connectorMock.Object);
var _svc = new MySvc(_repoMock.Object);
// This should return an object
_repoMock.Setup(r => r.GetObjectByIdAsync<Foo>(It.IsAny<int>(), _connectorMock.Object))
.ReturnsAsync(new Foo());
// This should return null
_repoMock.Setup(r => r.GetObjectByIdAsync<Bar>(It.IsAny<int>(), _connectorMock.Object))
.ReturnsAsync((Bar)null);
await _svc.DoBusinessLogic(0, 0);
}
}

TableControllers Unit test

So I'm trying to write a simple tablecontroller Unit test for my backend??
I havent been able to do so, all I've achieve is writing unit testing for ApiControllers but is there a way to write a Unit test for TableControllers?
What I'll like to do is this:
public class AuctionController : TableController<Auction>
{
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
MobileServiceContext context = new MobileServiceContext();
DomainManager = new EntityDomainManager<Auction>(context, Request);
}
// GET tables/Auction
public IQueryable<Auction> GetAllAuction()
{
return Query();
}
// GET tables/Auction/48D68C86-6EA6-4C25-AA33-223FC9A27959
public SingleResult<Auction> GetAuction(string id)
{
return Lookup(id);
}
// PATCH tables/Auction/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task<Auction> PatchAuction(string id, Delta<Auction> patch)
{
return UpdateAsync(id, patch);
}
// POST tables/Auction
public async Task<IHttpActionResult> PostAuction(Auction item)
{
Auction current = await InsertAsync(item);
return CreatedAtRoute("Tables", new { id = current.Id }, current);
}
// DELETE tables/Auction/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task DeleteAuction(string id)
{
return DeleteAsync(id);
}
}
and i wish to make a test controller like this one:
[TestClass]
public class AuctionControllerTests
{
private readonly AuctionController _controller;
public AuctionControllerTests()
{
_controller = new AuctionController();
}
[TestMethod]
public void Fetch_all_existing_items()
{
Assert.Equal(2, _controller.GetAllTodoItems().ToList().Count);
}
}
how can I possibly be able to get this to work??? Please I would appreciate your help a lot.
Yes. it is possible but you code is not unit testable. Here are the steps for you
Find a way inject your depedencies MobileServiceContext and DomainManager
You need to set up contexts and requests etc as in shown in the following code.
(Code assumes you are using Moq)
public class ControllerUnitTestBase<T> where T: Controller
{
private Action<RouteCollection> _routeRegistrar;
private Mock<HttpRequestBase> _mockRequest;
protected virtual Action<RouteCollection> RouteRegistrar
{
get { return _routeRegistrar ?? DefaultRouteRegistrar; }
set { _routeRegistrar = value; }
}
protected Mock<HttpRequestBase> MockRequest
{
get
{
if (_mockRequest == null)
{
_mockRequest = new Mock<HttpRequestBase>();
}
return _mockRequest;
}
}
public abstract T TargetController { get; }
protected void TargetSetup()
{
var routes = new RouteCollection();
RouteRegistrar(routes);
var responseMock = new Mock<HttpResponseBase>();
responseMock.Setup(x => x.ApplyAppPathModifier(It.IsAny<string>())).Returns((string url) => url);
var contextMock = new Mock<HttpContextBase>();
contextMock.SetupGet(x => x.Request).Returns(MockRequest.Object);
contextMock.SetupGet(x => x.Response).Returns(responseMock.Object);
contextMock.SetupGet(x => x.Session).Returns(Mock<HttpSessionStateBase>().Object);
TargetController.ControllerContext = new ControllerContext(contextMock.Object, new RouteData(), TargetController);
TargetController.Url = new UrlHelper(new RequestContext(contextMock.Object, new RouteData()), routes);
}
protected void DefaultRouteRegistrar(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });
}
}
Inherit from this code and make sure you call TargetSetup() before test execution ( maybe in test initialization (setup). And you are good to go as in:
[TestClass]
public class AuctionControllerTests: TestControllerBase<AuctionController>
{
public AuctionController TargetController {
get {return new AuctionController();//inject your mocked dependencies}}
[TestInitialize]
public void SetUp()
{
TargetSetup()
}
}
So Thanks for the mocking solution, It worked but I wrote a generic better solution without using mocking framework, I'll apply mocking framework later, right now I'll stick with fakes and real dbs for integration tests.
but firstable I wrote a Generic TableController in order to apply multiple EntityData and DbContext for those who had more than one Context, also you could apply a FakeContext thanks to the abstraction of interfaces but i havent applied to this example.
First This is my BaseController:
//This is an abstract class so we can apply inheritance to scalfolded tablecontrollers<T>.
public abstract class BaseController<TModel, TDbContext> : TableController<TModel> where TModel : class, ITableData
where TDbContext:DbContext, new()
{
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
var context = new TDbContext();
SetDomainManager(new EntityDomainManager<TModel>(context, Request));
}
public void SetDomainManager(EntityDomainManager<TModel> domainManager)
{
DomainManager = domainManager;
}
}
this is my scalfolded controller with my basecontroller applied!!!
public class AuctionController : BaseController<Auction, MobileServiceContext>
{
public IQueryable<Auction> GetAllAuction()
{
return Query();
}
// GET tables/Auction/48D68C86-6EA6-4C25-AA33-223FC9A27959
public SingleResult<Auction> GetAuction(string id)
{
return Lookup(id);
}
// PATCH tables/Auction/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task<Auction> PatchAuction(string id, Delta<Auction> patch)
{
return UpdateAsync(id, patch);
}
// POST tables/Auction
public async Task<IHttpActionResult> PostAuction(Auction item)
{
Auction current = await InsertAsync(item);
return CreatedAtRoute("Tables", new { id = current.Id }, current);
}
// DELETE tables/Auction/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task DeleteAuction(string id)
{
return DeleteAsync(id);
}
}
With my generic application I can apply any DbContext that way you could even apply FakeDbContexts in order to avoid SqlConnection or Cloud connection such as Azure which is the one I used in this example.
UPDATED MARCH 14th, 2018
All this two library are on my Backend project, now I'll show you my test project in order to Unit Test a TableController
public abstract class ControllerTestBase<TController, TModel, TDbContext> where TController : BaseController<TModel, TDbContext>, new()
where TModel : class, ITableData
where TDbContext: DbContext, new()
{
protected readonly TController Controller;
protected ControllerTestBase()
{
Controller = new TController();
Controller.Configuration = new HttpConfiguration();
Controller.Request = new HttpRequestMessage();
var context = new TDbContext();
Controller.SetDomainManager(new EntityDomainManager<TModel>(context, Controller.Request));
}
}
Ok thanks to this abstract class you can supress the initialize setup from the testing library because each time you run a test it will call the generic test constructor, setting up all the necessary requierements and thus avoid ArgumentNullExceptions and InvalidOperationExceptions such common problem for unit testing tablecontroller since isnt quite intuitive to initialize as an ApiController.
Finally if you modify this then you can run a test like this:
[TestClass]
public class AuctionControllerTest : ControllerTestBase<AuctionController, Auction, MobileServiceContext>
{
[TestMethod]
public void Fetch_All_Existing_Items()
{
Assert.AreEqual(1, Controller.GetAllAuction().ToList().Count);
}
}
thanks to my generic application you can now use this code as an example to be apply to your TableControllers and also if you follow the Interface Segregation Principle you could apply FakeDbContext to your Controllers.
For those who helped me thanks you opened my mind into coming with this solution!!!

Should I mock or fake my repository?

I have a controller called PostsController
public class PostsController : Controller
{
private const int PageSize = 8;
private readonly IPostsRepository repository;
public PostsController(IPostsRepository repository)
{
this.repository = repository;
}
public ViewResult Index(int pageNumber = 1)
{
var posts =
repository.All()
.Where(post => !post.Draft)
.OrderBy(post => post.PublishedAt);
var model =
posts.MapTo<PostViewModel>()
.ToPagedList(pageNumber, PageSize);
return View("Index", model);
}
public ActionResult Post(string slug)
{
var post =
repository.Find(slug);
if (post == null || post.Draft)
{
return HttpNotFound();
}
return View("Post", post.MapTo<PostViewModel>());
}
}
And a corresponding test fixture called PostsControllerTest
[TestFixture]
public class PostsControllerTest
{
private PostsController controller;
private Mock<IPostsRepository> repository;
[SetUp]
public void SetUp()
{
AutoMapperConfig.Configure();
repository = new Mock<IPostsRepository>();
controller = new PostsController(repository.Object);
}
[Test]
public void Index_ReturnsCorrectViewName()
{
var actual = controller.Index();
Assert.AreEqual(actual.ViewName, "Index");
}
[Test]
public void Index_ReturnsCorrectModel()
{
var result = controller.Index();
var actual = result.Model as PagedList<PostViewModel>;
Assert.NotNull(actual);
}
[Test]
public void Index_WithPageNumber_ReturnsCorrectViewName()
{
var actual = controller.Index(2);
Assert.AreEqual(actual.ViewName, "Index");
}
[Test]
public void Index_WithPageNumber_ReturnsCorrectModel()
{
var result = controller.Index(2);
var actual = result.Model as PagedList<PostViewModel>;
Assert.NotNull(actual);
}
[Test]
public void Post_ReturnsCorrectViewName()
{
repository.Setup(repo => repo.Find("abc"))
.Returns(new Post());
var actual = controller.Post("abc") as ViewResult;
Assert.NotNull(actual);
Assert.AreEqual(actual.ViewName, "Post");
}
[Test]
public void Post_ThatIsDraft_ReturnsNotFound()
{
var post = new Post { Draft = true };
repository.Setup(repo => repo.Find("abc"))
.Returns(post);
var actual = controller.Post("abc");
Assert.IsAssignableFrom<HttpNotFoundResult>(actual);
}
[Test]
public void Post_ThatDoesNotExist_ReturnNotFound()
{
var actual = controller.Post("abc");
Assert.IsAssignableFrom<HttpNotFoundResult>(actual);
}
[Test]
public void Post_ReturnsCorrectModel()
{
var post = new Post
{
Slug = "continuing-to-an-outer-loop",
Title = "Continuing to an outer loop",
Summary = "When you have a nested loop, sometimes",
Content = "When you have a nested loop, sometimes",
PublishedAt = DateTime.Now.AddDays(7),
Tags = new Collection<Tag> { new Tag { Name = "Programming" } }
};
repository.Setup(repo => repo.Find("continuing-to-an-outer-loop"))
.Returns(post);
var viewResult = (ViewResult)controller.Post("continuing-to-an-outer-loop");
var actual = viewResult.Model as PostViewModel;
Assert.NotNull(actual);
Assert.AreEqual(actual.Slug, post.Slug);
Assert.AreEqual(actual.Title, post.Title);
Assert.AreEqual(actual.Summary, post.Summary);
Assert.AreEqual(actual.Content, post.Content);
Assert.AreEqual(actual.PublishedAt, post.PublishedAt);
Assert.AreEqual(actual.Tags, post.Tags);
}
}
I learned to mock the repository in this way by observing how other projects arranged their tests. One particular example of this approach (from which I learned) can be found here. Personally I have found this approach to be somewhat laborious. Furthermore, since adopting this approach I stumbled upon this post that states that you should not mock your repository but fake it instead.
Now I am conflicted. Should I proceed to mock my repository of fake it instead?
I kindly ask that you include code examples that show how to fake and seed the repository in this case as I am not sure how to do so.
To pick a side, I'd say mocking is just fine and is less work than faking.
But to voice an opinion - without trying to be awkward - I'd say... neither.
Consider how much value is being added by a test which goes through your Index action without hitting a real repository - the vast majority of the code you're testing is in Linq and AutoMapper, both of which have already been heavily tested by other people.
I would recommended writing Integration tests which run through your controller actions, through your real repositories, hit your database and come back out the other side. That adds value and gives you some real assurance that your system (i.e. the bits you've written) actually works.
Finally, Jimmy Bogard (of AutoMapper fame) has a good blog entry about testing repositories here.

Write unit test cases in VS2012

I'm new to write unit test cases using VS2012.
Can someone help me to write unit test cases for below method?
public myclasstype getEmployeeById(int empid)
{
// this method will return employee objects
}
Just a general outline of what you can test on the GetEmployeeById method:
[TestMethod]
public void GetEmployeeById_Id_Employee()
{
Employee employee = mockManager.MockObject<Employee>().Object;
employee.DateOfBirth = new DateTime(1970, 1, 1, 0, 0, 0);
using (RecordExpectations recorder = new RecordExpectations())
{
var dataLayer = new DataLayer();
recorder.ExpectAndReturn(dataLayer.GetEmployeeById(1), employee);
}
var company = new Company();
Employee result = company.GetEmployeeById(1);
Assert.AreEqual(result.DateOfBirth, employee.DateOfBirth);
}
It's a pretty broad question, but lets say you have an Employee class...
public class Employee
{
private IEmployeeRepository _repo;
public Employee(IEmployeeRepository repo)
{
_repo = repo;
}
public Employee GetEmployeeById(int empid)
{
return _repo.GetEmployeeById(empid);
}
}
Your Test would then need to be something like...
[Test]
public void Employee_GetEmployee_By_Id()
{
IEmployeeRepository repo = new FakeEmployeeRepository();
var employeeClass = new Employee(repo);
var employee = employee.GetEmployeeById(1);
//verify employee detail
}
This is very basic, but gives you an idea. Obviously, you will have a Fake Employee Repository which will just return a pre-setup Employee, and in production you will have a real implementation of IEmployeeRepository which will connects to a DB for example.
public interface IEmployeeRepository
{
Employee GetEmployeeById(int id);
}
public class FakeEmployeeRepository : IEmployeeRepository
{
public Employee GetEmployeeById(int id)
{
return new Employee { ... };
}
}
This is all hand typed, so there's probably errors here...it's just to give an idea though.
Below are the steps
1. Add Unit test project, Solution explorer -> Add -> New Project -> Select Test from the template -> Unit Test project.
2. Download and add Reference to the Moq library you can do that by Nuget command below. To Get Nuget Package manager console, go to Tools Menu-> Library Package Manager Console -> Library Package Manager. This should show Nuget package manager console near to debug, error window.
install-package Moq
While hitting above command make sure that you have selected your test project on the project list on Nuget Package manager console.
Lets say you have defined classes as below
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
}
public interface IEmployeeRepository
{
Employee GetById(int id);
}
public interface IUnitOfWork
{
T GetById(int id) where T : new() ;
}
public class UnitOfWork : IUnitOfWork
{
// Implementation of IUnitOfWork
//public T GetById<T>(int id) where T: new();
//{
// return new T();
//}
}
public class EmployeeRepository : IEmployeeRepository
{
//You are injecting Unit Of Work here
public IUnitOfWork UnitOfWork { get; set; }
public Employee GetById(int id)
{
// Making call to database here;
return UnitOfWork.GetById<Employee>(id);
}
}
Add UnitTest to your UnitTest Project by right click on Unit Test project , Add -> Unit Test.
Below is sample code for your Unit Test based on your classes above.
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
namespace UnitTestProject1
{
[TestClass]
public class EmployeeUnitTest
{
Mock _unitOfWork;
IEmployeeRepository _employeeRepository;
//This will be called before each test
[TestInitialize]
public void SetUp()
{
_unitOfWork = new Mock<IUnitOfWork>();
_employeeRepository = new EmployeeRepository();
}
[TestMethod]
public void GetById_ShouldCallUnitOfWork()
{
//Arrange
const int Id = 1;
_unitOfWork.Setup(x => x.GetById<Employee>(It.IsAny<int>())).Verifiable();
//Act
_employeeRepository.GetById(Id);
//Assert
_unitOfWork.Verify(x => x.GetById<Employee>(Id), Times.Once());
}
[TestMethod]
public void GetById_ShouldRetrunEmployee()
{
//Arrange
const int Id = 1;
var expectedEmp = new Employee { Id = Id, Name= "Emp"};
_unitOfWork.Setup(x => x.GetById<Employee>(It.IsAny<int>())).Returns(expectedEmp) ;
//Act
var employee = _employeeRepository.GetById(Id);
//Assert
Assert.AreEqual(expectedEmp, employee);
}
}
}
Right clik on the method you want to write unit test for > Create UnitTest...
In Create Unit Test dialog
Tick off which methods you want to generate unit tests for:
[OK]
Enter a name for the test project
[Create]
Use Assert class to check the results

Unit Test Fails for Unit Of Work Test

This is my test:
[TestMethod]
public void TestUnitOfWork()
{
UnitOfWork unitOfWork = new UnitOfWork();
unitOfWork.ContactRepository.Insert(new Contact
{
Id = Guid.NewGuid(),
FirstName = "Dom",
LastName = "A",
Email = "dominicarchual#yahoo.com"
});
var contacts = unitOfWork.ContactRepository.Get(x => x.FirstName == "Dominic");
Assert.AreEqual(1, contacts.Count());
}
The error I get is:
Test method
MvcContacts.Tests.Controllers.HomeControllerTest.TestUnitOfWork threw
exception: System.Data.ProviderIncompatibleException: An error
occurred while getting provider information from the database. This
can be caused by Entity Framework using an incorrect connection
string. Check the inner exceptions for details and ensure that the
connection string is correct. --->
System.Data.ProviderIncompatibleException: The provider did not return
a ProviderManifestToken string. --->
System.Data.SqlClient.SqlException: A network-related or
instance-specific error occurred while establishing a connection to
SQL Server. The server was not found or was not accessible. Verify
that the instance name is correct and that SQL Server is configured to
allow remote connections. (provider: SQL Network Interfaces, error: 26
- Error Locating Server/Instance Specified)
I don't have any database set up; i.e. my context looks like this:
namespace MvcContacts.DAL
{
public class ContactsContext : DbContext
{
public DbSet<Contact> Contacts { get; set; }
}
}
I don't know exactly how to map this to my database; but, I was thinking that I wouldn't have to do that yet since I am just trying to test using mock data. Am I wrong?
E1: This is my unit of work class.
namespace MvcContacts.DAL
{
public class UnitOfWork : IDisposable
{
private ContactsContext context = new ContactsContext();
private GenericRepository<Contact> contactRepository;
public GenericRepository<Contact> ContactRepository
{
get
{
if (this.contactRepository == null)
{
this.contactRepository = new GenericRepository<Contact>(context);
}
return contactRepository;
}
}
public void Save()
{
context.SaveChanges();
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
context.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}
As i said, the problem is that you are actually calling real database inside your UnitOfWork. I'm pretty sure, your GenericRepository<> class just wraps DbSet inside your context. Here is where you create the 'real' database accessor.
private ContactsContext context = new ContactsContext();
But the problem is you misunderstand the whole concept of repositories. Unit of work is an abstraction of some data source. You should not unit test the abstraction, instead you should unit test some functionality which depends upon it. By the way, DbContext itself is a Unit of work by that definition (from martinfowler.com):
Maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems.
Why don't people just leave it as it is? Because there is a flaw in it. Let me explain by example. Seems like you're learning ASP.Net MVC so let's write some controller:
public class ContactsController
{
public ActionResult Index(int pageSize, int currentPage)
{
using(var db = new MvcLearningContext())
{
var contacts = db.Contacts
.Skip((currentPage - 1) * pageSize)
.Take(pageSize)
.ToList();
return View(contacts);
}
}
}
As you may know, one of the great advantages of MVC is the ability to unit test controller logic. So, let's try to write a simple unit test to make sure out controller action doesn't return more entries than the given page size:
[TestMethod]
public void IndexShouldNotReturnMoreThanPageSizeResults()
{
// arrange
var controller = new ContactsController();
// act
var view = (ViewResult) controller.Index(10, 1);
// assert
var Model = (IEnumerable<Contact>) view.Model;
Assert.IsTrue(view.Model.Count() <= 10)
}
But wait... We do not want to query the real database in the unit test. Here comes the problem with EF's DbContext: it completely depends on real database. But how can we avoid that? UnitOfWork comes in play:
public class ContactsController
{
private UnitOfWorkFactoryBase _factory { get; set; }
public ContactsController(UnitOfWorkFactoryBase factory)
{
factory = _factory;
}
public ActionResult Index(int pageSize, int currentPage)
{
using(var db = _factory.Create())
{
var contacts = db.Contacts
.Skip((currentPage - 1) * pageSize)
.Take(pageSize)
.ToList();
return View(contacts);
}
}
}
unit test code:
[TestMethod]
public void IndexShouldNotReturnMoreThanPageSizeResults()
{
// arrange
var factory = new MockUnitOfWorkFactory();
var controller = new ContactsController(factory);
// act
var view = (ViewResult) controller.Index(10, 1);
// assert
var Model = (IEnumerable<Contact>) view.Model;
Assert.IsTrue(view.Model.Count() <= 10)
}
and in production you replace MockUnitOfWorkFactory with UnitOfWorkFactory
UPD: basic implementation of factories:
public abstract class UnitOfWorkFactoryBase
{
public abstract UnitOfWorkBase Create();
}
public class UnitOfWorkFactory : UnitOfWorkFactoryBase
{
public override UnitOfWorkBase Create()
{
return new UnitOfWork();
}
}
public class MockUnitOfWorkFactory : UnitOfWorkFactoryBase
{
public override UnitOfWorkBase Create()
{
return new MockUnitOfWork();
}
}
UnitOfWork and MockUnitOfWork are implementations of UnitOfWorkBase abstract class.

Categories

Resources