I am just getting started with unit testing and now stuck writing a test method for delete. I am using JustMock. Below I have mentioned my controller action method and test method but the test method is not working. The return value of the method is not retaining it's always returning false. Please guide me on what I'm doing wrong here.
This is the test method.
[TestMethod]
public void Delete_User()
{
Mapper.CreateMap<UserViewModel,User>();
IStaticMembershipService membershipRepository = Mock.Create<IStaticMembershipService>();
var mockUserViewModel = new UserViewModel { Id = 1};
var mockUser = Mapper.Map<UserViewModel, User>(mockUserViewModel);
var UserRepository = Mock.Create<IUserRepository>();
Mock.Arrange(() => UserRepository.DeleteUser(mockUser)).Returns(new OperationStatus { Status = true }).MustBeCalled();
UserController controller = new UserController(UserRepository, membershipRepository);
ActionResult result = controller.Delete(mockUserViewModel);
Mock.Assert(() => UserRepository.DeleteUser(mockUser), Occurs.AtLeastOnce());
}
Any relevant controller info.
private IUserRepository _UserRepository;
private IStaticMembershipService _membershipService;
public UserController(IUserRepository UserRepo, IStaticMembershipService membershipService)
{
_UserRepository = UserRepo;
_membershipService = membershipService;
}
The repository is mocked correctly but the "opStatus" property getting returned is always false not the value I have specified to be the result in testmethod.
public ActionResult Delete(UserViewModel usr)
{
var selectedUser = Mapper.Map<UserViewModel, User>(usr);
var opStatus = _UserRepository.DeleteUser(selectedUser);
if (!opStatus.Status)
throw new System.Web.Http.HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
return RedirectToAction("Index");
}
I hope i have provided enough information. Any help much appreciated.
Change:
Mock.Arrange(() => UserRepository.DeleteUser(mockUser)).Returns(new OperationStatus { Status = true }).MustBeCalled();
to:
Mock.Arrange(() => UserRepository.DeleteUser(mockUser)).IgnoreArguments().Returns(new OperationStatus { Status = true }).MustBeCalled();
Alternatively, add equality operators to User so that it realises that mockUser and selectedUser are the 'same'. Or use lambdas for argument matching (see http://www.telerik.com/blogs/handling-arguments-in-justmock-arrangements).
Related
I'm trying to unit test an action in an MVC controller but the mocked object is returning null.
I have a ClientProxy interface
public interface IClientProxy
{
Task<T> Get<T>(string uri);
}
With the following concrete implementation
public async Task<T> Get<T>(string uri)
{
using (HttpClient httpClient = new HttpClient())
{
ConfigureClient(httpClient);
var response = await httpClient.GetAsync(uri);
response.EnsureSuccessStatusCode();
return await Task.Run(() => GetResultFromResponse<T>(response));
}
}
This is my action
public async Task<ActionResult> Index()
{
var categories = await _proxy.Get<PagedResults<Category>>("/category/get");
return View("index", null, JsonConvert.SerializeObject(categories));
}
And this is the unit test class
public class CategoryControllerTests
{
private readonly CategoryController _controller;
private readonly Mock<IClientProxy> _mockProxy;
public CategoryControllerTests()
{
_mockProxy = new Mock<IClientProxy>();
_controller = new CategoryController(_mockProxy.Object);
}
[Fact]
public async Task IndexPageRenders()
{
// Arrange
var fakeResult = new PagedResults<Category>
{
Paging = new Paging
{
Page = 1,
PageSize = 10,
TotalRecords = 2
},
Results = new List<Category>
{
new Category { CategoryId = 1, Name = "Category One" },
new Category { CategoryId = 2, Name = "Category Two" }
}
};
var result = JsonConvert.SerializeObject(fakeResult);
_mockProxy.Setup(p => p.Get<PagedResults<Category>>(It.IsAny<string>())).ReturnsAsync(fakeResult);
// Act
var action = await _controller.Index() as ViewResult;
// Assert
_mockProxy.Verify(p => p.Get<PagedResults<Category>>(It.IsAny<string>()), Times.Exactly(1));
}
}
Currently the mocked object setup for the get method is not getting hit, and categories are null (the Verify is failing, implying that the mocked method is never called). However, if I remove the await keyword from the action then the categories are returned. I'd rather not remove the await just to pass the test as it's there for a good reason, so any help would be appreciated.
Thanks in advance.
Currently the mocked object setup for the get method is not getting hit, and categories are null (the Verify is failing, implying that the mocked method is never called).
That would mean that the mocked object was not passed as a dependency to the controller being tested and as such is not being invoked when the test is being exercised.
Here is a Minimal, Complete, and Verifiable example that demonstrates how one would setup and exercise a test in such a scenario.
The follow were used
public class MyModel {
public int MyIntProperty { get; set; }
public string MyStringProperty { get; set; }
}
public interface IClientProxy {
Task<T> Get<T>(string uri);
}
public class MyController : Controller {
IClientProxy _proxy;
public MyController(IClientProxy _proxy) {
this._proxy = _proxy;
}
public async Task<ActionResult> Index() {
var categories = await _proxy.Get<MyModel>("/category/get");
return View(categories);
}
}
And following test was used with MSTest, Moq and Fluent Assertions where is flowed to completion and passed.
[TestClass]
public class MyController_Should {
[TestMethod]
public async Task _Render_Index_Page() {
// Arrange
var fakeResult = new MyModel {
MyIntProperty = 0,
MyStringProperty = "Hello World."
};
var _mockProxy = new Mock<IClientProxy>();
_mockProxy
.Setup(_ => _.Get<MyModel>(It.IsAny<string>()))
.ReturnsAsync(fakeResult);
var _controller = new MyController(_mockProxy.Object);
// Act
var action = await _controller.Index() as ViewResult;
// Assert
_mockProxy.Verify(_ => _.Get<MyModel>(It.IsAny<string>()), Times.Exactly(1));
action.Should().NotBeNull();
var model = action.Model;
model.Should().NotBeNull()
.And.BeOfType<MyModel>()
.And.Be(fakeResult);
}
}
I would suggest reviewing this example and then comparing it to how you designed your test to see if you can identify what may be causing your problem.
How can i test a method which clear the session and logoff user.
my controller method looks like
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
SessionAdapter.Clear();
SessionAdapter.Abandon();
AuthenticationManager.SignOut
(DefaultAuthenticationTypes.ApplicationCookie);
return RedirectToAction("Index", "Home");
}
Here session adapter is my static class
public static class SessionAdapter
{
private static string sessionKey = "sessionInfoKey";
public static SessionInfo Instance
{
get
{
return HttpContext.Current.Session[sessionKey] == null ? null : (SessionInfo)HttpContext.Current.Session[sessionKey];
}
set
{
HttpContext.Current.Session[sessionKey] = value;
}
}
public static bool DoesSessionExists { get { return HttpContext.Current.Session[sessionKey] == null ? false : true; } }
public static void Clear()
{
HttpContext.Current.Session.Clear();
}
}
Please help me
In a unit test you should mocking out session since it's an external dependency. Your unit tests should be testing your code, not the .net framework.
So a valid test might be to verify that a call to Session.Clear happened, not actually testing Session.Clear clears out a session.
This can be done by setting up a fake session. Here's a extension method I use to setup my controller context for unit testing
public static void SetControllerContext(this Controller controller)
{
var fakeContext = A.Fake();
var fakeRequest = A.Fake();
var fakeResponse = A.Fake();
var fakeSessionState = A.Fake();
A.CallTo(() => fakeRequest.HttpMethod).Returns(HttpVerbs.Post.ToString());
A.CallTo(() => fakeContext.Response).Returns(fakeResponse);
A.CallTo(() => fakeContext.Request).Returns(fakeRequest);
A.CallTo(() => fakeContext.Session).Returns(fakeSessionState);
var fakeRequestContext = new RequestContext(fakeContext, new RouteData());
controller.ControllerContext = new ControllerContext(fakeRequestContext, controller);
}
This is using FakeItEasy, but the same thing can be done with Moq.
From MS "ASP.NET session state enables you to store and retrieve values for a user as the user navigates ASP.NET pages in a Web application."
What's the value in wrapping that in a static class?
I want to test WebMethod of some Web Service (asmx). Suppose I have the following code:
public IUsersRepository UsersRepository
{
get { return Session["repository"] as IUsersRepository; }
set { Session["repository"] = value; }
}
[WebMethod(EnableSession = true)]
public int AddUser(string userName, int something)
{
var usersRepository = Session["repository"] as IUsersRepository;
return usersRepository.AddUser(userName, something);
}
and the corresponding unit test (just to test that the repository is called at all):
[Test]
public void add_user_adds_user()
{
// Arrange
var repository = new Mock<IUsersRepository>();
var service = new ProteinTrackingService { UsersRepository = repository.Object };
// Act
var userName = "Tester";
var something = 42;
service.AddUser(userName: userName, something: something);
// Assert
repository.Verify(r => r.AddUser(
It.Is<string>(n => n.Equals(userName)),
It.Is<int>(p => p.Equals(something))));
}
When I run this test, I receive the following error message:
System.InvalidOperationException : HttpContext is not available.
This class can only be used in the context of an ASP.NET request.
What shall I do to make this test working?
Have you had a look at this one? Setting HttpContext.Current.Session in a unit test Apparently should can do that trick to simulate your session.
On regards to your assert, you can directly do:
// Assert
repository.Verify(r => r.AddUser(userName, something));
And that will assert you are calling that method with these parameters.
Hope this helps!
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.
Here's a sample of one of my unit test classes (pared down to the basics). In the controller, when the Index() action method is invoked, a call to GetByID(1234) always results in a newed up instance of a Ticket object. The object exists, but all of its properties are null, even though I've set them in my fake object. Any ideas as to why?
I'm using Moq.
Unit test
[TestClass]
public class TicketControllerTests : ControllerTestBase
{
protected Mock<ITicketRepository> MockTicketRepository = new Mock<ITicketRepository>();
[TestMethod]
public void IndexActionModelIsTypeOfTicketModel()
{
//ARRANGE
Mock<HttpContextBase> context = FakeHttpContext();
context.Setup(ctx => ctx.Session[SessionKeys.TokenData.ToString()]).Returns(Constants.TOKENDATA_SUBMITTER);
MockTicketRepository.Setup(x => x.GetById(It.IsAny<int>())).Returns(Constants.CLIENT_TICKET);
//ACT
var result = GetController(context.Object).Index(Constants.TICKET_ID);
var model = ((ViewResult)result).Model;
//ASSERT
Assert.IsInstanceOfType(model, typeof(TicketModel), "ViewModel should have been an instance of TicketModel.");
}
private TicketController GetController(HttpContextBase context)
{
var controller = new TicketController(MockTicketRepository.Object);
controller.ControllerContext = GetControllerContext(context, controller);
return controller;
}
}
Constants.CLIENT_TICKET
public static Ticket CLIENT_TICKET
{
get
{
var ticket = new Ticket
{
CategoryID = 1,
CreatedByUserId = 4
};
ticket.Clients.Add(new Client { ShortName = "Test Client 1"});
ticket.Clients.Add(new Client { ShortName = "Test Client 2" });
ticket.User = new User {FirstName = "First", LastName = "Last"};
return ticket;
}
}
Controller
private readonly ITicketRepository _ticketRepository;
public TicketController(ITicketRepository ticketRepository)
{
_ticketRepository = ticketRepository;
}
public ActionResult Index(int id)
{
var ticket = _ticketRepository.GetById(id);
// etc...
}
Could you show the controller code under test? It could be related to how you have set up the mocked context but it's hard to tell without seeing the controller code.
Also, if you add MockBehavior.Strict when you create the mock, it will bomb out if the invocation doesn't have a corresponding expectation:
protected Mock<ITicketRepository> MockTicketRepository = new Mock<ITicketRepository>(MockBehavior.Strict);
UPDATE
I've tried to strip everything back so that the test is as simple as possible to try and isolate the issue. Here's what I have come up with:
[TestClass]
public class TicketControllerTests : ControllerTestBase
{
protected Mock<ITicketRepository> MockTicketRepository;
[TestMethod]
public void IndexActionModelIsTypeOfTicketModel()
{
//ARRANGE
MockTicketRepository = new Mock<ITicketRepository>(MockBehavior.Strict);
MockTicketRepository.Setup(x => x.GetById(Constants.TICKET_ID)).Returns(Constants.CLIENT_TICKET);
var controller = new TicketController(MockTicketRepository.Object);
//ACT - try to keep ACT as lean as possible, ideally just the method call you're testing
var result = controller.Index(Constants.TICKET_ID);
//ASSERT
var model = ((ViewResult)result).ViewData.Model;
Assert.That(model, Is.InstanceOfType<TicketModel>(), "ViewModel should have been an instance of TicketModel.")
}
}