Reputation: 1187
I am testing a controller.
That is my controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Pmanager.Models;
using RBravoDLL;
using System.Globalization;
using Resources;
using Pmanager.Helpers;
namespace Pmanager.Controllers
{
[Authorize]
public class GroupController : DefaultController
{
private Group m_group;
public Group p_group
{
get
{
if (m_group == null)
{
m_group = new Group();
}
return m_group;
}
set
{
m_group = value;
}
}
[HttpPost]
[AllowAdminOperationsOnly]
public ActionResult NewGroup(Group _group)
{
try
{
int id_group = 0;
if (ModelState.IsValid)
{
id_group = p_group.NewGroup(_group);
}
else
{
ThrowErrorMessages();
}
return Json(new
{
status = "success",
title = @Resources.Global.success,
text = @Resources.Groups.success_creating_group,
messageType = "success",
id_group = id_group
}, JsonRequestBehavior.AllowGet);
}
catch (Exception err)
{
return ErrorHelper(@Resources.Global.error, @Resources.Groups.error_creating_group, err.Message);
}
}
}
}
That is my model:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Resources;
using System.Security.AccessControl;
using Pmanager.DAL;
namespace Pmanager.Models
{
public enum Permission
{
Not = 0, Read = 1, Analyse = 2, Edit = 3, Admin = 4
}
public class Group : IGroup
{
public int ID { get; set; }
[Required]
[StringLength(50)]
[Index(IsUnique = true)]
public string Name { get; set; }
[Required]
public bool IsOperations { get; set; }
public Permission? OperationPermission { get; set; }
public virtual ICollection<UserGroup> UserGroups { get; set; }
public int NewGroup(Group group)
{
RioBravoManagerContext ctx = new RioBravoManagerContext();
ctx.Groups.Add(group);
ctx.SaveChanges();
return group.ID;
}
public void UpdateGroup(Group group)
{
RioBravoManagerContext ctx = new RioBravoManagerContext();
if (ctx.Groups.Where(g => g.ID == group.ID).Any())
{
Group cgroup = ctx.Groups.Where(g => g.ID == group.ID).SingleOrDefault();
cgroup.Name = group.Name;
cgroup.IsOperations = group.IsOperations;
cgroup.OperationPermission = group.OperationPermission;
}
else
{
ctx.Groups.Add(group);
}
ctx.SaveChanges();
}
}
public interface IGroup
{
void UpdateGroup(Group group);
int NewGroup(Group group);
}
}
The test class is:
using System.Web.Mvc;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Pmanager.Controllers;
using Pmanager.Tests.Helpers;
using System.Web.Script.Serialization;
using System.Collections.Generic;
using Pmanager.Models;
using System;
using Moq;
namespace Pmanager.Tests.Controllers
{
[TestClass]
public class GroupControllerTest
{
JavaScriptSerializer m_serializer;
ControllerContext m_context;
[TestInitialize]
public void TestInitialize()
{
m_serializer = new JavaScriptSerializer();
m_context = TestModelHelper.AdminControllerContext();
}
public void GroupController_UpdateGroup_Valid()
{
// Arrange
GroupController controller = new GroupController();
controller.ControllerContext = m_context;
var _group = new Mock<IGroup>();
_group.Setup(g => g.UpdateGroup(It.IsAny<Group>())).Callback((Group group) => { });
controller.p_group = _group.Object as Group;
Group _gp = new Group
{
ID = 1,
Name = "Group Name",
IsOperations = false,
OperationPermission = Permission.Read
};
// Act
var result = (JsonResult)controller.UpdateGroup(_gp);
JsonGroup resultFund = m_serializer.Deserialize<JsonGroup>(m_serializer.Serialize(result.Data));
// Assert
Assert.IsNotNull(result.Data, "There should be some data for the JsonResult");
Assert.IsNotNull(resultFund.status, "JSON record does not contain 'status' required property.");
Assert.IsTrue(resultFund.status.Equals("success"), "status must be 'success'");
}
[TestMethod]
public void GroupController_UpdateGroup_Invalid()
{
// Arrange
GroupController controller = new GroupController();
controller.ControllerContext = m_context;
var _group = new Mock<IGroup>();
_group.Setup(g => g.UpdateGroup(It.IsAny<Group>())).Callback((Group group) => { });
controller.p_group = _group.Object as Group;
controller.ModelState.AddModelError("Name", @"Missing Name");
Group _gp = new Group
{
ID = 1,
IsOperations = false,
OperationPermission = Permission.Read
};
// Act
var result = (JsonResult)controller.UpdateGroup(_gp);
JsonGroup resultFund = m_serializer.Deserialize<JsonGroup>(m_serializer.Serialize(result.Data));
// Assert
Assert.IsNotNull(result.Data, "There should be some data for the JsonResult");
Assert.IsNotNull(resultFund.status, "JSON record does not contain 'status' required property.");
Assert.IsTrue(resultFund.status.Equals("fail"), "status must be 'fail'");
}
[TestMethod]
public void GroupController_NewGroup_Valid()
{
// Arrange
GroupController controller = new GroupController();
controller.ControllerContext = m_context;
var _fakegroup = new Mock<IGroup>() { CallBase = false };
_fakegroup.Setup(g => g.NewGroup(It.IsAny<Group>())).Returns<Group>((group) => { return 0; }).Verifiable();
controller.p_group = _fakegroup.Object as Group;
Group _gp = new Group
{
ID = 0,
Name = "Group Name",
IsOperations = false,
OperationPermission = Permission.Read
};
// Act
var result = (JsonResult)controller.NewGroup(_gp);
JsonGroup resultFund = m_serializer.Deserialize<JsonGroup>(m_serializer.Serialize(result.Data));
// Assert
Assert.IsNotNull(result.Data, "There should be some data for the JsonResult");
Assert.IsNotNull(resultFund.status, "JSON record does not contain 'status' required property.");
Assert.IsTrue(resultFund.status.Equals("success"), "status must be 'success'");
}
[TestMethod]
public void GroupController_NewGroup_Invalid()
{
// Arrange
GroupController controller = new GroupController();
controller.ControllerContext = m_context;
var _group = new Mock<IGroup>();
_group.Setup(g => g.NewGroup(It.IsAny<Group>())).Returns((Group group) => { return 0; });
controller.p_group = _group.Object as Group;
controller.ModelState.AddModelError("Name", @"Missing Name");
Group _gp = new Group
{
ID = 1,
IsOperations = false,
OperationPermission = Permission.Read
};
// Act
var result = (JsonResult)controller.NewGroup(_gp);
JsonGroup resultFund = m_serializer.Deserialize<JsonGroup>(m_serializer.Serialize(result.Data));
// Assert
Assert.IsNotNull(result.Data, "There should be some data for the JsonResult");
Assert.IsNotNull(resultFund.status, "JSON record does not contain 'status' required property.");
Assert.IsTrue(resultFund.status.Equals("fail"), "status must be 'fail'");
}
}
public class JsonGroup
{
public string status { get; set; }
public List<SimpleGroup> groups { get; set; }
}
}
The issue is: the mock on the method is not working! It is running the actual method when called. Basically:
var _fakegroup = new Mock<IGroup>() { CallBase = false };
_fakegroup.Setup(g => g.NewGroup(It.IsAny<Group>())).Returns<Group>((group) => { return 0; }).Verifiable();
What should I do instead?
Upvotes: 1
Views: 2907
Reputation: 247018
Your classes should have a single responsibility. One reason to change. one common job. The example looks like you are using the Group
as both a model/DTO and a service that provides functions/methods.
Strip functionality out into its own concern.
public interface IGroupService {
void UpdateGroup(Group group);
int NewGroup(Group group);
}
public class GroupService : IGroupService {
public int NewGroup(Group group) {
RioBravoManagerContext ctx = new RioBravoManagerContext();
ctx.Groups.Add(group);
ctx.SaveChanges();
return group.ID;
}
public void UpdateGroup(Group group) {
RioBravoManagerContext ctx = new RioBravoManagerContext();
if (ctx.Groups.Where(g => g.ID == group.ID).Any()) {
Group cgroup = ctx.Groups.Where(g => g.ID == group.ID).SingleOrDefault();
cgroup.Name = group.Name;
cgroup.IsOperations = group.IsOperations;
cgroup.OperationPermission = group.OperationPermission;
} else {
ctx.Groups.Add(group);
}
ctx.SaveChanges();
}
}
Keep models lean. Their sole responsibility should be to hold information to be transferred.
public enum Permission {
Not = 0, Read = 1, Analyse = 2, Edit = 3, Admin = 4
}
public class Group {
public int ID { get; set; }
[Required]
[StringLength(50)]
[Index(IsUnique = true)]
public string Name { get; set; }
[Required]
public bool IsOperations { get; set; }
public Permission? OperationPermission { get; set; }
public virtual ICollection<UserGroup> UserGroups { get; set; }
}
Let the controller depend on the service and inject it via constructor.
[Authorize]
public class GroupController : DefaultController {
private readonly IGroupService groupService;
public GroupController(IGroupService groupService) {
this.groupService = groupService;
}
[HttpPost]
[AllowAdminOperationsOnly]
public ActionResult NewGroup(Group _group) {
try {
int id_group = 0;
if (ModelState.IsValid) {
id_group = groupService.NewGroup(_group);
} else {
ThrowErrorMessages();
}
return Json(new
{
status = "success",
title = @Resources.Global.success,
text = @Resources.Groups.success_creating_group,
messageType = "success",
id_group = id_group
}, JsonRequestBehavior.AllowGet);
} catch (Exception err) {
return ErrorHelper(@Resources.Global.error, @Resources.Groups.error_creating_group, err.Message);
}
}
}
Test can not be performed in isolation for the given method under test
[TestMethod]
public void GroupController_NewGroup_Valid() {
// Arrange
var _fakegroup = new Mock<IGroupService>();
_fakegroup.Setup(g => g.NewGroup(It.IsAny<Group>())).Returns(1).Verifiable();
var controller = new GroupController(_fakegroup.Object);
var _gp = new Group {
ID = 0,
Name = "Group Name",
IsOperations = false,
OperationPermission = Permission.Read
};
// Act
var result = controller.NewGroup(_gp) as JsonResult;
// Assert
_fakegroup.Verify();
Assert.IsNotNull(result.Data, "There should be some data for the JsonResult");
dynamic data = result.Data;
Assert.IsNotNull(data.status, "JSON record does not contain 'status' required property.");
Assert.IsTrue(data.status.Equals("success"), "status must be 'success'");
}
And finally remember to register the service abstraction and implementation with the DI container.
Upvotes: 3
Reputation: 14677
Change your properties in the Controller from Type Group
to IGroup
.
private IGroup m_group;
public IGroup p_group
{
get
{
if (m_group == null)
{
m_group = new Group();
}
return m_group;
}
set
{
m_group = value;
}
}
In the test:
controller.p_group = _fakegroup.Object;
This will solve your problem. You're using dependencies as concrete types instead of abstraction(Interface) in the controller.
Upvotes: 1