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!!!
Related
I am trying to understand how mocking works in Xunit with AutoFixture. I have created Service and Repository classes and their interfaces. Mocked method should pass value which is different from default value.
Mocked method always pass default values instead of values which i am writing in ".Returns()". I have tried AutoConfiguredMoqCustomization but it provides completely random values which i can't get back.
Repository.cs
public class Repository : IRepository
{
public int GetInt()
{
return 999;
}
}
Service.cs
public class Service : IService
{
private readonly Repository _repository;
public Service()
{
_repository = new Repository();
}
public string GetStringFromInt()
{
return _repository.GetInt().ToString();
}
}
Test
[Fact]
public void Test()
{
var fixture = new Fixture().Customize(new AutoMoqCustomization());
var repositoryMock = fixture.Create<Mock<IRepository>>();
var service = fixture.Create<Service>();
repositoryMock.Setup(x => x.GetInt()).Returns(1);
var act = service.GetStringFromInt();
Assert.Equal("1", act);
}
As you see value by default in Repository is 999 and I am expecting 1 from repositoryMock but result is "999" instead of "1".
Ow I have understood my problem. When I declare parameters with auto moq testing service must be AFTER all mocked repositories
Test
[Theory, AutoMoqData]
public void Test([Frozen] Mock<IRepository> repositoryMock, Service service)
{
...
}
Attribute
public class AutoMoqDataAttribute : AutoDataAttribute
{
public AutoMoqDataAttribute() : base(GetDefaultFixture)
{
}
private static IFixture GetDefaultFixture()
{
return new Fixture().Customize(new AutoMoqCustomization());
}
}
You should freeze your mock first. When you call Create on AutoFixture, it will create you a new instance every time. Try the following in the modified code (where you are using an interface of the type in your constructor).
public class ServiceTests
{
private readonly IFixture fixture = new Fixture().Customize(new AutoMoqCustomization());
public ServiceTests()
{
fixture.Register<IService>(() => fixture.Create<Service>());
}
[Fact]
public void Test()
{
// Arrange
var repositoryMock = fixture.Freeze<Mock<IRepository>>();
repositoryMock.Setup(x => x.GetInt()).Returns(1);
var service = fixture.Create<IService>();
// Act
var act = service.GetStringFromInt();
// Verify
Assert.Equal("1", act);
}
}
To check that you have set up autofixture correctly, you can try the following in the unit test in future.
var repo1 = fixture.Create<IRepository>();
var repo2 = fixture.Create<IRepository>();
Assert.Equal(repo1.GetHashCode(), repo2.GetHashCode());
If the above fails, that indicates that you have not frozen a type. These lines of code have saved me so much head scratching in the past...
You are doing DI wrong, you are not injecting Repository into Your serice.
Try like this.
public class Repository : IRepository
{
public int GetInt()
{
return 999;
}
}
public class Service : IService
{
IRepository Repository;
public Service(IRepository repository)
{
this.Repository = repository;
}
public string GetStringFromInt()
{
return Repository.GetInt().ToString();
}
}
Now when you mock IRepository, you can add it to Service.
You are using a new Repository() in constructor, so you are using that implementation
I'm a sitecore developer, and I want to create a sample sitecore helix unit testing project for testing out the exact below Index() action method of our "HomeBottomContentController" controller, without any dependency injection into a constructor. Note that the commented-out code is exactly what I do NOT want to do.
public class HomeBottomContentController : GlassController
{
// I want to test the EXACT method below
public override ActionResult Index()
{
var context = new SitecoreContext();
var model = context.GetCurrentItem<Home_Control>();
return View("~/Views/HomeBottomContent/HomeBottomContent.cshtml", model);
}
/*
// I do NOT want to have any of the below code for injecting ISitecoreContext into a constructor and testing the IndexTest() below it.
private readonly ISitecoreContext _iSitecoreContext;
public HomeBottomContentController(ISitecoreContext iSitecoreContext)
{
_iSitecoreContext = iSitecoreContext;
}
public HomeBottomContentController()
{ }
public ActionResult IndexTest()
{
var model = _iSitecoreContext.GetCurrentItem<Home_Control>();
return View("~/Views/HomeBottomContent/HomeBottomContent.cshtml", model);
}
*/
}
Here's what I have in my unit testing class. Again, what I have commented out exactly what I don't want to do:
[TestClass]
public class MvcUnitTests
{
[TestMethod]
public void Test_HomeBottomContentController_With_ISitecoreContext()
{
/*
// I don't want to do below...
var model = new Home_Control()
{ Bottom_Content = "XYZ" };
var iSitecoreContext = new Mock<Glass.Mapper.Sc.ISitecoreContext>();
iSitecoreContext.Setup(_ => _.GetCurrentItem<Home_Control>(false, false)).Returns(model);
HomeBottomContentController controllerUnderTest = new HomeBottomContentController(iSitecoreContext.Object);
var result = controllerUnderTest.IndexTest() as ViewResult;
*/
//I want to test using the exact constructor below and calling that exact Index() method.
HomeBottomContentController controllerUnderTest = new HomeBottomContentController();
var result = controllerUnderTest.Index() as ViewResult;
Assert.IsNotNull(result);
Assert.IsNotNull(result.Model);
//Assert.AreEqual(((Home_Control)result.Model).Bottom_Content, "XYZ");
}
}
How can I test my controller's exact Index() method without having to add code to the HomeBottomContentController class that enables dependency injection into a constructor (like the commented-out code above)? I do not want to have to add code to HomeBottomContentController().
#Aleksey Shevchenko If I try your solution, how do I exactly hook up the model to the iSitecoreContext and then assign that to the controllerUnderTest.FakeContext? My code below throws compilation error (You cannot convert from Mock of Glass.Mapper.Sc.ISitecoreContext to Glass.Mapper.Sc.ISitecoreContext, how do we accomplish that):
var model = new Home_Control()
{ Top_Content = "Some Dummy Test Home Top Content" };
var iSitecoreContext = new Mock<Glass.Mapper.Sc.ISitecoreContext>();
//var iSitecoreContext = new Glass.Mapper.Sc.SitecoreContext();
iSitecoreContext.Setup(_ => _.GetCurrentItem<Home_Control>(false, false)).Returns(model);
FakeHomeTopContentController controllerUnderTest = new FakeHomeTopContentController();
controllerUnderTest.FakeContext = (Glass.Mapper.Sc.SitecoreContext)iSitecoreContext;
If you don't want to create dependency injection through constructor you can do that through protected virtual method. Something like that:
public class HomeBottomContentController : GlassController
{
public override ActionResult Index()
{
var context = this.GetContext();
var model = context.GetCurrentItem<Home_Control>();
return View("~/Views/HomeBottomContent/HomeBottomContent.cshtml", model);
}
protected virtual SitecoreContext GetContext()
{
return new SitecoreContext();
}
}
[TestClass]
public class MvcUnitTests
{
[TestMethod]
public void Test_HomeBottomContentController_With_ISitecoreContext()
{
var controllerUnderTest = new FakeHomeBottomContentController();
controllerUnderTest.FakeContext = /* set what you want */;
var result = controllerUnderTest.Index() as ViewResult;
Assert.IsNotNull(result);
Assert.IsNotNull(result.Model);
}
public class FakeHomeBottomContentController : HomeBottomContentController
{
public SitecoreContext FakeContext { get; set; }
protected override SitecoreContext GetContext()
{
return this.FakeContext;
}
}
}
I don't know the limitations of your ability to end the source code but based on your comment to the other answer you have access to it. I would opt for Poor Man's DI, have two constructors on the controller. The unit test uses the second constructor and MVC will use the parameterless constructor.
public class HomeBottomContentController : GlassController
{
ISitecoreContext _iSitecoreContext;
public HomeBottomContentController() :this(new SitecoreContext()){
}
public HomeBottomContentController(ISitecoreContext iSitecoreContext)
{
_iSitecoreContext = iSitecoreContext;
}
public ActionResult IndexTest()
{
var model = _iSitecoreContext.GetCurrentItem<Home_Control>();
return View("~/Views/HomeBottomContent/HomeBottomContent.cshtml", model);
}
}
[TestClass]
public class MvcUnitTests
{
[TestMethod]
public void Test_HomeBottomContentController_With_ISitecoreContext()
{
var model = new Home_Control()
{ Bottom_Content = "XYZ" };
var iSitecoreContext = new Mock<Glass.Mapper.Sc.ISitecoreContext>();
iSitecoreContext.Setup(_ => _.GetCurrentItem<Home_Control>(false, false)).Returns(model);
HomeBottomContentController controllerUnderTest = new HomeBottomContentController(iSitecoreContext.Object);
var result = controllerUnderTest.IndexTest() as ViewResult;
}
I'm trying to mock SignalR broadcasting present in ApiController(WebApi), but unable to complete test case, below is my code
SignalRHub
public class HubServer : Hub { }
ApiControllerWithHub
public abstract class ApiControllerWithHubController<THub> : ApiController where THub : IHub
{
Lazy<IHubContext> hub = new Lazy<IHubContext>(() => GlobalHost.ConnectionManager.GetHubContext<THub>());
protected IHubContext Hub
{
get { return hub.Value; }
}
}
Controller (Method to Mock)
public class NotificationController : ApiControllerWithHubController<HubServer>
{
[HttpPost]
public HttpResponseMessage SendNotification(NotificationInput notification)
{
Hub.Clients.Group("GroupName").BroadcastCustomerGreeting("notification");
}
}
I'm writing following unit test with the help of Mock SignalR Post, I'm stuck here because this is SignalR call from controller not from SignalR Hub.
MockTest
public interface IClientContract
{
void BroadcastCustomerGreeting(string message);
}
[TestMethod]
public void SendNotificationTest()
{
NotificationInput notificationInput = new NotificationInput();
notificationInput.CId = "CUST001";
notificationInput.CName = "Toney";
// Arrange
var mockClients = new Mock<IHubConnectionContext<dynamic>>();
var mockGroups = new Mock<IClientContract>();
// Act.
mockGroups.Setup(_ => _.BroadcastCustomerGreeting("notification")).Verifiable();
mockClients.Setup(_ => _.Group("GroupName")).Returns(mockGroups.Object);
// I'm stuck here
var controller = new NotificationController();
// Act
HttpResponseMessage actionResult = controller.SendNotification(notificationInput);
}
Any help is appreciated to complete/correct this unit test.
Redesign needed. Base ApiController tightly coupled to static accessor of the hub context. This needs to be refactored out into its own service to allow for more flexibility via constructor injection.
public interface IHubContextProvider {
IHubContext Hub { get; }
}
public class HubContextProvider<THub> : IHubContextProvider where THub : IHub {
Lazy<IHubContext> hub = new Lazy<IHubContext>(() => GlobalHost.ConnectionManager.GetHubContext<THub>());
public IHubContext Hub {
get { return hub.Value; }
}
}
Controllers now need to be refactored to explicitly expose its dependencies.
public abstract class ApiControllerWithHubController<THub> : ApiController where THub : IHub {
private readonly IHubContext hub;
public ApiControllerWithHubController(IHubContextProvider context) {
this.hub = context.Hub;
}
protected IHubContext Hub {
get { return hub; }
}
}
public class NotificationController : ApiControllerWithHubController<HubServer> {
public NotificationController(IHubContextProvider context)
: base(context) {
}
[HttpPost]
public IHttpActionResult SendNotification(NotificationInput notification) {
Hub.Clients.Group("GroupName").BroadcastCustomerGreeting("notification");
return Ok();
}
}
Test can now be exercised with necessary mocks of dependencies.
[TestMethod]
public void _SendNotificationTest() {
// Arrange
var notificationInput = new NotificationInput();
notificationInput.CId = "CUST001";
notificationInput.CName = "Toney";
var groupName = "GroupName";
var message = "notification";
var mockGroups = new Mock<IClientContract>();
mockGroups.Setup(_ => _.BroadcastCustomerGreeting(message)).Verifiable();
var mockClients = new Mock<IHubConnectionContext<dynamic>>();
mockClients.Setup(_ => _.Group(groupName)).Returns(mockGroups.Object).Verifiable();
var mockHub = new Mock<IHubContext>();
mockHub.Setup(_ => _.Clients).Returns(mockClients.Object).Verifiable();
var mockHubProvider = new Mock<IHubContextProvider>();
mockHubProvider.Setup(_ => _.Hub).Returns(mockHub.Object);
var controller = new NotificationController(mockHubProvider.Object);
// Act
var actionResult = controller.SendNotification(notificationInput);
//Assert
mockClients.Verify();
mockGroups.Verify();
mockHub.Verify();
}
Just make sure to register new service with DI container so that it can be injected into dependent controllers.
With the redesign the base controller can be removed all together and the hub provider used directly. This is assuming that there was not any other reason to have the base controller.
I'm new to unit testing, so my problem is probably with my code and not the Moq framework, but here goes.
I'm using .Net Core with xUnit and the Moq framework, and I'm more or less following instructions from their documentation. I'm trying to test route api/user to get all users, and the issue was on asserting that the response was an ObjectResult containing <IEnumerable<User>>. No matter what I tried, result.Value was always null. The first assertion passes fine.
I set up a console project to debug this, and found something interesting. that value of the controller in the test method in Visual Studio is null. In VS Code, the value in the debugger shows Unknown Error: 0x00000....
Below is the test:
public class UserControllerTests {
[Fact]
public void GetAll_ReturnsObjectResult_WithAListOfUsers() {
// Arrange
var mockService = new Mock<IUserService>();
var mockRequest = new Mock<IServiceRequest>();
mockService.Setup(svc => svc.GetUsers(mockRequest.Object))
.Returns(new ServiceRequest(new List<User> { new User() }));
var controller = new UserController(mockService.Object);
// Act
var result = controller.GetAll();
// Assert
Assert.IsType<ObjectResult>(result);
Assert.IsAssignableFrom<IEnumerable<User>>(((ObjectResult)result).Value);
}
}
And here is the controller:
public class UserController : Controller {
private IUserService service;
public UserController(IUserService service) {
this.service = service;
}
[HttpGet]
public IActionResult GetAll() {
var req = new ServiceRequest();
service.GetUsers(req);
if(req.Errors != null) return new BadRequestObjectResult(req.Errors);
return new ObjectResult(req.EntityCollection);
}
}
And the Service Layer:
public interface IUserService {
IServiceRequest GetUsers(IServiceRequest req);
}
public class UserService : IUserService {
private IUserRepository repo;
public IServiceRequest GetUsers(IServiceRequest req) {
IEnumerable<User> users = null;
try {
users = repo.GetAll();
}
catch(MySqlException ex) {
req.AddError(new Error { Code = (int)ex.Number, Message = ex.Message });
}
finally {
req.EntityCollection = users;
}
return req;
}
}
public interface IServiceRequest {
IEnumerable<Object> EntityCollection { get; set; }
List<Error> Errors { get; }
void AddError(Error error);
}
public class ServiceRequest : IServiceRequest {
public IEnumerable<Object> EntityCollection { get; set; }
public virtual List<Error> Errors { get; private set; }
public ServiceRequest () { }
public void AddError(Error error) {
if(this.Errors == null) this.Errors = new List<Error>();
this.Errors.Add(error);
}
}
Like I said, it's probably something I'm doing wrong, I'm thinking in the mockService.Setup() but I'm not sure where. Help please?
From the use of service.GetUsers(req) it looks like service is suppose to populate the service request but in your setup you have it returning a service request. A result which is also not used according to your code.
You need a Callback to populate whatever parameter is given to the service in order to mock/replicate when it is invoked. Since the parameter is being created inside of the method you will use Moq's It.IsAny<> to allow the mock to accept any parameter that is passed.
var mockService = new Mock<IUserService>();
mockService.Setup(svc => svc.GetUsers(It.IsAny<IServiceRequest>()))
.Callback((IServiceRequest arg) => {
arg.EntityCollection = new List<User> { new User() };
});
This should allow the method under test to flow through it's invocation and allow you to assert the outcome.
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.