BDD with SubSpec and XUnit NullReferenceException - bdd

Fairly new to TDD; want to give BDD a try first. I'm using an MVP UI Presentation Pattern Framework and I'm trying to write my first test using SubSpec and XUnit but I'm getting a NullReferenceException from my presenter when I make a call to the repo.
I'm sure the answer is obvious, but its got me. Also, it appears that my test is concerned more with the details of the Presentation Pattern -- I trust that it works and probably doesn't need to be tested like below (i.e. raising the view.load event) but I could'nt think of another way. Any suggestions for a better test are welcomed.
My Unit Test:
[Specification]
public void ViewLoad_WhenTheView.LoadEventIsRaised_ViewLoadShouldGetAll()
{
var view = MockRepository.GenerateMock<IOpenJobsView>();
var repository = MockRepository.GenerateMock<IOpenJobsRepository>();
var model = new OpenJobsModel().OpenJobs;
var openJobs = new List<OpenJob>();
var jobsFromModel = view.Stub(v => v.Model.OpenJobs).Return(model);
var jobsFromRepo = repository.Stub(r => r.GetAll()).Return(openJobs);
var presenter = default(OpenJobsPresenter);
"Given an openJobsPresenter"
.Context(() => presenter = new OpenJobsPresenter(view, repository));
"when the view loads"
.Do(() => view.Raise(v => v.Load += presenter.ViewLoad, view, new EventArgs()));
"the object subscribed to the event should result in a call to GetAll"
.Assert(() => repository.AssertWasCalled(o => o.GetAll()));
"the results from the call to GetAll should be equal to the model"
.Assert(() => Assert.Equal(jobsFromModel, jobsFromRepo));
My Presenter:
public class OpenJobsPresenter : Presenter<IOpenJobsView>
{
readonly IOpenJobsRepository openJobsRepository;
public OpenJobsPresenter(IOpenJobsView view, IOpenJobsRepository openJobsRepository) : base(view)
{
this.openJobsRepository = openJobsRepository;
View.Load += ViewLoad;
}
public void ViewLoad(object sender, System.EventArgs e)
{
View.Model.OpenJobs = openJobsRepository.GetAll(); //Getting NullReferenceException here
}
}

Ok. I was able to figure this out. The null reference was due to the model not being initialized by the view. So I stubbed out the model on the view -- that did it.
I cleaned up my test and included it along with the system under test for a complete solution.
Unit Test:
public class OpenJobsPresenterTests
{
readonly OpenJobsModel _model;
readonly IOpenJobsView _view;
readonly IOpenJobsRepository _repo;
public OpenJobsPresenterTests()
{
_model = MockRepository.GenerateMock<OpenJobsModel>();
_view = MockRepository.GenerateMock<IOpenJobsView>();
_repo = MockRepository.GenerateMock<IOpenJobsRepository>();
}
[Specification]
public void OpenJobsPresenterShouldGetAllOpenJobsOnViewLoad()
{
var presenter = default(OpenJobsPresenter);
var openJobs = new List<OpenJob>();
_view.Stub(v => v.Model).Return(_model);
_repo.Stub(d => d.GetAll()).Return(openJobs);
"Given the OpenJobsPresenter"
.Context(() => presenter = new OpenJobsPresenter(_view, _repo));
"when the view's load event is raised"
.Do(() => _view.Raise(d => d.Load += presenter.OnViewLoad, _view, new EventArgs()));
"the event subscriber should get all open jobs"
.Assert(() => _repo.AssertWasCalled(r => r.GetAll()));
"the model should equal the results returned"
.Assert(() => _view.Model.OpenJobs.ShouldBe(openJobs));
}
}
SUT:
public class OpenJobsPresenter : Presenter<IOpenJobsView>
{
readonly IOpenJobsRepository openJobsRepository;
public OpenJobsPresenter(IOpenJobsView view, IOpenJobsRepository openJobsRepository) : base(view)
{
this.openJobsRepository = openJobsRepository;
View.Load += OnViewLoad;
}
public void OnViewLoad(object sender, System.EventArgs e)
{
View.Model.OpenJobs = openJobsRepository.GetAll();
}
}

Related

How to mock HttpContext in ASP.NET Core [duplicate]

This question already has answers here:
Error trying to create Mock.Of<ControllerContext>() for ASP.Net Core 3.1 Unit Test
(2 answers)
Closed 1 year ago.
I'm trying to mock HttpContext to test my UsersController.
The UsersController inherit from
public abstract class ControllerBase
and HttpContext is a property of ControllerBase
public HttpContext HttpContext { get; }
and here is the method in the UsersContoller, which I want to test
public async Task<IActionResult> Register([FromBody] UserViewModel model)
{
_logger.LogDebug("Register new user");
var user = mapper.Map<User>(model);
user.Company.Active = false;
var result = await userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await userManager.AddToRoleAsync(user, Roles.NO_ACCESS);
//send confirmation email
string confirmationToken = userManager.GenerateEmailConfirmationTokenAsync(user).Result;
HostString hostString = HttpContext.Request.Host; //I need to mock httpcontext for this
this.mailSender.SendConfirmationMailAsync(user, hostString, confirmationToken);
return Ok();
}
else
{
_logger.LogInformation("User could not be registered Errors:");
result.Errors.ToList().ForEach(e => _logger.LogInformation(e.Description));
return BadRequest(result.Errors);
}
}
this is my BaseTestContoller, in which setup for tests is initialized
[SetUp]
public void Setup()
{
var dbContext = CreateDbContext();
CreateUserManager();
CreateMailSender(dbContext);
CreateMockImapper();
CreateMockIlogger();
usersController = new Mock<UsersController>(
userManagerMock.Object,
new CompanyService(dbContext),
mailSenderMock,
new Mock<IConfiguration>().Object,
iMapperMock.Object,
iLoggerFactoryMock.Object);
}
i've tried many options, but it wasn't successful therefor it would be nice if someone could help me.
Thanks in advance
UPDATE
usersController = new Mock<UsersController>(
userManagerMock.Object,
new CompanyService(dbContext),
mailSenderMock,
new Mock<IConfiguration>().Object,
iMapperMock.Object,
iLoggerFactoryMock.Object);
var conterllerContext = new ControllerContext() { HttpContext = new DefaultHttpContext() { } };
HostString host = new HostString("test.de");
conterllerContext.HttpContext.Request.Host = host;
usersController.Setup(c => c.HttpContext).Returns(conterllerContext.HttpContext);
Now i have a problem with setting up.
userController.setup returns this msg :
System.NotSupportedException : Unsupported expression: c => c.HttpContext
Non-overridable members (here: ControllerBase.get_HttpContext) may not be used in setup / verification expressions.
You can use ControllerContext to set the context to be DefaultHttpContext which you can modify to your needs.
var ctx = new ControllerContext() { HttpContext = new DefaultHttpContext()};
var tested = new MyCtrl();
tested.ControllerContext = ctx;
Can't you make a mock service for getting the host address (I am not really familiar with mock libraries).
Something like this:
class IHostService {
string GetHost();
}
public class HostService {
// create constructor with httpcontextaccessor
public string GetHost() {
return _httpContextAccessor.HttpContext.Request.Host;
}
}
public class MockHostService {
public string GetHost() {
return "test.de";
}
}
and in your mock class you can probably add this service just like you added mailSenderMock. And in your controller you use string host = _hostService.GetHost().

Mvc.net Unit test with a nullable parameter

I am new for unit test with Moq and Nunit. I have a simple unit test for getting a product list. But the test result didn't turn out as expected.
ProductManagementController action is:
public ViewResult Index(int? id)
{
return View(_ProductRepo.GetProductList(id));
}
ProductRepository is:
public IList<Product> GetProductList(int? id)
{
if (id == null)
{
return (db.Products).ToList();
}
else
{
return db.Products.Where(i => i.CategoryId == id).ToList();
}
}
I have two tests setup, but only the first test(get all products list) is fine.
[TestFixture]
public class Test_ProductManagement
{
private List<Product> products;
private List<Category> categories;
private Mock<IProductRepository> mockProducts;
private ProductManagementController target;
[TestFixtureSetUp]
public void Initialize()
{
images = new List<Product> {
new Product{Id = 1, Name = "Boat", CategoryId = 1 },
new Product{Id = 2, Name = "Picture Frame",CategoryId = 2 }
};
categories = new List<Category> {
new Category { Id = 1, Name = "Outdoors" },
new Category { Id = 2, Name = "Housewares" }
};
mockProducts = new Mock<IProductRepository>();
mockProducts.Setup(m => m.GetProductList(It.IsAny<int>())).Returns(products);
target = new ProductManagementController(mockProducts.Object);
}
[Test]
public void Index_Contains_All_Products()
{
//Action
List<Product> result = ((IEnumerable<Product>)target.Index(It.IsAny<int>()).ViewData.Model).ToList();
//Assert
Assert.AreEqual(2, result.Length);
Assert.AreEqual("Boat", result[0].Name);
Assert.AreEqual("Picture Frame", result[1].Name);
}
[Test]
public void Index_Get_ImageList_By_CategoryId()
{ //Arrange
int id = 2;
//Action
List<Product> result = ((IEnumerable<Product>)target.Index(id).ViewData.Model).ToList();
//Assert
Assert.AreEqual(1, result.Count());
Assert.AreEqual("Picture Frame", result[0].Name);
}
The 2nd test always return a full product list which include 2 products while I only expect 1 returned from Assert.AreEqual(1,result.Count());
I can't figure out why test code always take the id as a null parameter in the above index call: target.Index(id) . However all my project codes run correctly in the browsers.
Does anyone has a clue? Thanks.
I can't figure out why test code always take the id as a null parameter in the above index call: target.Index(id)
It doesn't. You don't show all code (specifically how your controller handles the repository injection) and you seem to have renamed at least the products / images field, but the code works just fine.
You call target.Index(id), which in turn calls _ProductRepo.GetProductList(id). I assume this is your mock being called, which is Setup() by you to always return the entire product list.
The mock does exactly what you ask it to, no ProductRepository is used in this code, so your if (id == null) is never executed.
In order to fix this, you must Setup() your mock differently in each test method, or setup both cases in advance:
mockProducts.Setup(m => m.GetProductList(null)).Returns(products);
mockProducts.Setup(m => m.GetProductList(2)).Returns(new List<Product>{products[1]});
Of course you want to setup the latter call somewhat less specific, but I hope you see where you can fix this.

How to use Automapper to map controller methods to viewmodel properties for a asp.net mvc application?

I am trying to leverage the Automapper for mapping the controller methods to the viewmodel properties in an asp.net mvc project.
I analyzed few articles about Automapper and found that it is wonderful object to object mapper between complex model object and viewmodel object.
I have code in a controller: CustomersController.cs
[Authorize(Roles = "Administrator")]
public ActionResult Index()
{
var user = _userService.GetUser(_profile.UserName);
if (!user.IsActive)
return RedirectToAction("");
var clientGroups = new List<ClientGroup>();
var model = new CustomerGroupsIndexViewModel()
{
CustomerGroupUsersUrl = Url.RouteUrl<CustomerGroupsController>(c => c.GetUsersForCustomerGroup(null, null, null, 0, 0)),
CustomerGroupByAreaUrl = Url.RouteUrl<CustomerGroupsController >(c => c.GetAreaDetailsForCustomerGroup(null, null, null, 0, 0)),
CheckLoginNameUrl = Url.RouteUrl<UsersController>(c => c.CheckLoginName(null)),
ResetUserUrl = Url.RouteUrl<UsersController>(c => c.ResetPassword(null)),
GetSelectOptionsForCustomerGroupUrl = Url.RouteUrl<ClientGroupsController>(c => c.GetSelectOptionsForCustomerGroup(null,null)),
FindUsersMatchingTermUrl = Url.RouteUrl<UsersController>(c => c.FindUsersMatchingWithLoginName(null)),
NumberOfTestTaken = _scanService.GetCustomerForUser(user).Count(),
RefreshCustomerGroupUrl = Url.RouteUrl<CustomerGroupsController >(c => c.RefreshClientGroup()),
};
Response.Cache.SetExpires(DateTime.UtcNow.AddMinutes(-1));
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetNoStore();
return View("CustomerGroupIndex", model);
}
I have such methods across the project code base.
Can anyone help me how can I use Automapper efficiently here?
Thanks & Regards,
Santosh Kumar Patro
I 'll explain it very briefly
Create configuration for each Model and related ViewModel like this
Interface for configuration:
interface IGlobalConfiguration
{
void Configure();
}
Class for configuration:
public class AutoMapperViewModelConfiguration : IGlobalConfiguration
{
public void Configure()
{
Mapper.CreateMap<Model1,ViewModel1>();
Mapper.CreateMap<ViewModel1,Model1>()
.ForMember(x => x.ModelMember1, y => y.Ignore());//Ignore if not required
}
}
Create a class and method like this:
public class GlobalConfigurationModule
{
private readonly Assembly assembly;
public GlobalConfigurationModule()
: this(Assembly.GetExecutingAssembly())
{
}
public GlobalConfigurationModule(Assembly assembly)
{
this.assembly = assembly;
}
public void Load()
{
var ins = from x in assembly.GetExportedTypes()
where x.GetInterfaces().Contains(typeof(IGlobalConfiguration))
select Activator.CreateInstance(x) as IGlobalConfiguration;
foreach (var config in ins)
{
config.Configure();
}
}
}
Call the Load method in Global.asax
protected void Application_Start()
{
new GlobalConfigurationModule().Load();
}
When you require to map a Model to ViewModel use this Mapper.Map() method like this:
Mapper.Map(model1Object, viewModel1Object);
Edit:
If you need the mapping should be done from a method you can create mapping like this.
Mapper.CreateMap<Source, Destination>()
.ForMember(dest => dest.SomeValue,
opt => opt.MapFrom(src => src.GetSomeValue()))
You cannot map a method returns void.
Cheers

Mocking Ajax.IsRequest to return False

I am trying to mock the Ajax.IsRequest() method of ASP.Net MVC. I found out how to do it in order for it to return true:
Expect.Call(_myController.Request.Headers["X-Requested-With"]).Return("XMLHttpRequest").Repeat.Any();
This works and returns true. Now I need to test the other branch of the code. How can I mock it to return false? I have tried removing the mock altogether, It fails with:
System.NullReferenceException : Object
reference not set to an instance of an
object.]
If I do:
Expect.Call(_templateReportController.Request["X-Requested-With"]).Return(null).Repeat.Any();
It fails with the same error.
Entire Test:
/// <summary>
/// Tests the Edit Action when calling via Ajax
/// </summary>
[Test]
public void Test_Edit_AjaxRequest()
{
Group group = new Group();
group.ID = 1;
group.Name = "Admin";
IList<Group> groupList = new List<Group>() { group };
Definition def = new Definition();
def.ID = 1;
def.Name = "Report";
def.LastModified = DateTime.UtcNow;
def.Groups.Add(group);
using (_mocks.Record())
{
Expect.Call(_myController.Request["X-Requested-With"]).Return("XMLHttpRequest").Repeat.Any();
Expect.Call(_DefBiz.GetAll<Group>()).Return(groupList);
Expect.Call(_DefBiz.Get<Definition>(1)).Return(def);
}
myController.DefAccess = _DefBiz;
PartialViewResult actual;
using (_mocks.Playback())
{
actual = (PartialViewResult)myController.Edit(1);
}
}
Any advices?
Cheers
The reason your are getting NullReferenceException is because you never stubbed the controller.Request object in your unit test and when you invoke the controller action which uses Request.IsAjaxRequest() it throws.
Here's how you could mock the context using Rhino.Mocks:
[TestMethod]
public void Test_Ajax()
{
// arrange
var sut = new HomeController();
var context = MockRepository.GenerateStub<HttpContextBase>();
var request = MockRepository.GenerateStub<HttpRequestBase>();
context.Stub(x => x.Request).Return(request);
// indicate AJAX request
request.Stub(x => x["X-Requested-With"]).Return("XMLHttpRequest");
sut.ControllerContext = new ControllerContext(context, new RouteData(), sut);
// act
var actual = sut.Index();
// assert
// TODO: ...
}
[TestMethod]
public void Test_Non_Ajax()
{
// arrange
var sut = new HomeController();
var context = MockRepository.GenerateStub<HttpContextBase>();
var request = MockRepository.GenerateStub<HttpRequestBase>();
context.Stub(x => x.Request).Return(request);
sut.ControllerContext = new ControllerContext(context, new RouteData(), sut);
// act
var actual = sut.Index();
// assert
// TODO: ...
}
And here's a better alternative (which I would personally recommend you) in order to avoid all the plumbing code. Using MVCContrib.TestHelper (which is based on Rhino.Mocks) your unit test might be simplified to this:
[TestClass]
public class HomeControllerTests : TestControllerBuilder
{
private HomeController _sut;
[TestInitialize()]
public void MyTestInitialize()
{
_sut = new HomeController();
this.InitializeController(_sut);
}
[TestMethod]
public void HomeController_Index_Ajax()
{
// arrange
_sut.Request.Stub(x => x["X-Requested-With"]).Return("XMLHttpRequest");
// act
var actual = _sut.Index();
// assert
// TODO: ...
}
[TestMethod]
public void HomeController_Index_Non_Ajax()
{
// act
var actual = _sut.Index();
// assert
// TODO: ...
}
}
Much prettier. It also allows you to write much more expressive asserts on the action results. Checkout the doc or ask if for more info is needed.
Expect.Call(_myController.Request.Headers["X-Requested-With"]).Return("SpitAndDuctTape").Repeat.Any();
...should do the job.

Using Moq to test methods that accept non-primative arguments

I'm trying to write a test for an ASP.Net MVC controller action.
I'd like to test that the action invokes a particular method on an injected service, so I'm mocking the service and using .Verify.
So in the simple case, I have the following action in my controller:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(string title)
{
_cmsService.AddPage(title);
return View("Edit");
}
using the service interface...
public interface ICmsService
{
void AddPage(string request);
}
and the following test...
//Arrange
const string pageTitle = "Test Page";
var cmsService = new Mock<ICmsService>();
cmsService.Setup(service => service.AddPage(pageTitle));
var controller = new PageController(cmsService.Object);
//Act
var result = controller.Create(pageTitle) as ViewResult;
//Assert
cmsService.Verify(service => service.AddPage(pageTitle), Times.Once());
Now I want to refactor my service operation to use request and response objects...
public interface ICmsService
{
CmsServiceAddPageResponse AddPage(CmsServiceAddPageRequest request);
}
So I change my action accordingly...
public ActionResult Create(string title)
{
var request = new CmsServiceAddPageRequest()
{
PageName = title
};
var response = _cmsService.AddPage(request);
return View("Edit");
}
and also my test...
//Arrange
const string pageTitle = "Test Page";
var cmsService = new Mock<ICmsService>();
var request = new CmsServiceAddPageRequest() {PageName = pageTitle};
cmsService.Setup(service => service.AddPage(request));
var controller = new PageController(cmsService.Object);
//Act
var result = controller.Create(pageTitle) as ViewResult;
//Assert
cmsService.Verify(service => service.AddPage(request), Times.Once());
But now when I run the test, I get the following message...
TestCase 'Web.Test.PageControllerTest.CreateNewPagePost'
failed: Moq.MockException :
Invocation was performed more than once on the mock: service => service.AddPage(value(Web.Test.PageControllerTest+<>c__DisplayClass1).request)
at Moq.Mock.ThrowVerifyException(IProxyCall expected, Expression expression, Times times)
at Moq.Mock.VerifyCalls(Interceptor targetInterceptor, MethodCall expected, Expression expression, Times times)
at Moq.Mock.Verify[T,TResult](Mock mock, Expression`1 expression, Times times, String failMessage)
at Moq.Mock`1.Verify[TResult](Expression`1 expression, Times times)
PageControllerTest.cs(67,0): at Web.Test.PageControllerTest.CreateNewPagePost()
What should I be doing to test a method that accepts a non-primitive type?
Thanks
Sandy
I think a better alternative to the first answer would be to implement a custom matcher rather than change code to match your testing framework. From:http://code.google.com/p/moq/wiki/QuickStart
// custom matchers
mock.Setup(foo => foo.Submit(IsLarge())).Throws<ArgumentException>();
...
public string IsLarge()
{
return Match<string>.Create(s => !String.IsNullOrEmpty(s) && s.Length > 100);
}
If you override Equals in the CmsServiceAddPageRequest object it should compare them correctly.

Resources