I've created a new test project in a pre-existing Visual Studio solution and attempting to mock a controller so I can test routing. The main project makes use of Combres for minification of css etc. To better demonstrate the problem I've put the .AddCombresRoute into the test which generates the error I'm trying to solve.
private HttpContextBase rmContext;
private HttpRequestBase rmRequest;
[TestInitialize]
public void SetupTests()
{
// Setup Rhino Mocks
rmContext = MockRepository.GenerateMock<HttpContextBase>();
rmRequest = MockRepository.GenerateMock<HttpRequestBase>();
rmContext.Stub(x => x.Request).Return(rmRequest);
}
[TestMethod]
public void RhinoMocksRoutingTest()
{
// Arrange
RouteCollection routes = new RouteCollection();
RouteConfig.RegisterRoutes(routes);
rmRequest.Stub(e => e.AppRelativeCurrentExecutionFilePath).Return("~/Home/Index");
// Act
routes.AddCombresRoute("Combres Route"); *** ERRROR HERE ***
RouteData routeData = routes.GetRouteData(rmContext);
// Assert
Assert.IsNotNull(routeData);
Assert.AreEqual("Home",routeData.Values["controller"]);
Assert.AreEqual("Index",routeData.Values["action"]);
}
Despite making the correct references, ensuring combres.xml and combres.xsd are in App_Data (and copied to local) and dropping in the relevant entries into app.config I get the following error when I run the test:
ArgumentNullException was unhandled by user code. An exception of type
'System.ArgumentNullException' occurred in System.Xml.dll was not
handled in user code. Additional information: Value cannot be null.
Related
In Moq, I want to assert that a method TestMethod is called with a specific parameter. When the test fails, I want to see a useful error message similar to:
TestMethod called with unexpected value X for parameter P where Y expected.
public interface ITestObject
{
void TestMethod(int parameter);
}
Just for sake of illustration, I could achieve this using a handcoded mock as follows:
Assert.AreEqual failed. Expected:<3>. Actual:<2>. TestMethod called with unexpected value for parameter 'actual'.
public class MockTestMethodParameter : ITestObject
{
private readonly int expected;
public MockTestMethodParameter(int expected) { this.expected = expected; }
public void TestMethod(int actual)
{
Assert.AreEqual(actual, expected, $"{nameof(TestMethod)} called with unexpected value for parameter '{nameof(actual)}'.");
}
}
[TestMethod]
public void TestHandcodedMockFailure()
{
var mock = new MockTestMethodParameter(expected: 2);
mock.TestMethod(actual: 3);
}
My problem is, I can't figure out how to do this in Moq. When I set up my Mock and call Verify(), I get the following unexpected and unclear error message, instead of the message I was hoping for:
Expected invocation on the mock at least once, but was never performed: test => test.TestMethod(It.Is(2, GenericEqualityComparer))
Performed invocations:
MockUnitTest1.ITestObject:1 (test):
UnitTest1.ITestObject.TestMethod(3)
[TestMethod]
public void TestMoqFailure()
{
var expected = 2;
var actual = 3;
var mock = new Moq.Mock<ITestObject>(Moq.MockBehavior.Strict);
mock.Setup(test => test.TestMethod(Moq.It.IsAny<int>())).Verifiable();
mock.Object.TestMethod(actual);
mock.Verify(test => test.TestMethod(Moq.It.Is(expected, EqualityComparer<int>.Default)));
}
Granted, the information is there, but I expected something more along the lines of, "TestMethod was invoked, but with incorrect parameters." It confuses me when Moq reports that TestMethod was not invoked because, intuitively, I did invoke the mock. I called TestMethod with It.IsAny(), as declared in the mock setup.
I've tried many different adjustments, but none yielding the desired result:
Custom error message
Setup with Is(3)
Setup with Is(2)
MockBehavior.Loose
Different .NET platforms, Framework 4.6.1, Core 2.1, .NET 6.0
Is this simply the way Moq reports error results for Verify()? I'm new to Moq, and while this behavior is unexpected to me, perhaps it is working as designed.
This appears to be normal behavior for mock frameworks in general. Doing the same thing with Telerik JustMock yields a similar error message:
Occurrence expectation failed. Expected exactly 1 call. Calls so far: 0
[TestMethod]
public void TestJustMockFailure()
{
var expected = 2;
var actual = 3;
var mock = Mock.Create<ITestObject>(Behavior.Strict);
Mock.Arrange(() => mock.TestMethod(Arg.IsAny<int>()));
mock.TestMethod(actual);
Mock.Assert(() => mock.TestMethod(Arg.Is(expected)), Occurs.Once());
}
In summary:
The OP's use case (my use case) is valid, verifying a particular parameter value was passed to a mocked method. However, the error message does not report the parameter mismatch as such. Rather, it reports that the entire mocked method call did not occur. This is by design, and appears to be common among popular mock frameworks.
I'm writing an unit test(using NUnit & MOQ) for an action method MethodUnderTest which uses HttpContext internally to do some functionality. I'm setting up a fake hosting environment by calling InitializeHostingEnvironment where I'm initializing the session like:
public static HttpSessionState InitializeSession()
{
var httpRequest = new HttpRequest("", "http://localhost/", "");
var stringWriter = new StringWriter();
var httpResponse = new HttpResponse(stringWriter);
var httpContext = new HttpContext(httpRequest, httpResponse);
HttpContext.Current = httpContext;
HttpContext.Current.Items.Clear();
HttpSessionState session = (HttpSessionState)ReflectionHelper.Instantiate(typeof(HttpSessionState), new Type[] { typeof(IHttpSessionState) }, new FakeHttpSessionState());
HttpContext.Current.Items.Add("AspSession", session);
return session;
}
public static void InitializeHostingEnvironment(string userName, string password)
{
// lines of code
InitializeSession();
}
I'm calling the InitializeHostingEnvironment() from my Test Method like so:
public static void Test_MethodUnderTest()
{
InitializeHostingEnvironment(UN, PW);
MethodUnderTest(param1, param2, param3); -- getting exception while trying to execute this line
}
While trying to execute the line MethodUnderTest(param1, param2, param3);, I'm getting an exception - System.ArgumentNullException - Value cannot be null. Parameter name httpBrowserCapabilities. Stack trace is given below:
Since the exception says httpBrowserCapabilities is null, I tried to initialize it like HttpContext.Current.Request.Browser = new HttpBrowserCapabilities(); inside the InitializeSession() method, but now, I'm getting another exception:
What should I do now? Is the way I'm initializing HttpContext wrong? please advise.
I'm writing an unit test(using NUnit & MOQ) for a method MethodUnderTest which uses HttpContext internally to do some functionality.
Right. Don't do that. The controller should be the only method that accesses the HttpContext, and it should extract the data needed for your MethodUnderTest and use the to write to the HttpContext.
Structuring your code so that each method has a single responsibility is fundamental to writing testable code.
So I built unit tests for an ApiController, and one of those fails only on the server in the CI pipeline with the exception System.InvalidOperationException : HttpControllerContext.Configuration must not be null.
The structure looks like this:
public IHttpActionResult MyControllerMethod()
{
if(this.SomeThings()) // uses this.IService and this.IService2
return this.UnAuthorized();
DoOtherStuff();
return this.Ok();
}
In my test, I mock IService and IService2 and pass them to the controller:
var controllerUnderTest = new MyController(serviceMock, serviceMock2);
Then I call the method with some invalid data, provoking the return of UnAuthorized():
var result = await controllerUnderTest.MyControllerMethod(invalidData).ExecuteAsync(CancellationToken.None);`
At first, this failed. So I added
controllerUnderTest.Configuration = new HttpConfiguration();
controllerUnderTest.Request = new HttpRequestMessage();
from this Stackoverflow post.
So now it works. Locally. It works when debugging, it works using ReSharper's testrunner, it works using NCrunch.
But it doesn't work in the pipeline.
When executed in the pipeline using ncrunch console tool, the tests fails with above message.
Any idea how to fix this? How can config be nul when I explicitely initialize it?
Edit: Here is the stacktrace:
System.InvalidOperationException : HttpControllerContext.Configuration must not be null.
bei System.Web.Http.Results.ExceptionResult.ApiControllerDependencyProvider.EnsureResolved()
bei System.Web.Http.Results.ExceptionResult.ApiControllerDependencyProvider.get_IncludeErrorDetail()
bei System.Web.Http.Results.ExceptionResult.Execute()
bei System.Web.Http.Results.ExceptionResult.ExecuteAsync(CancellationToken cancellationToken)
I am familiarizing myself with the asp.net C# unit test framework and I have quickly encountered issues when using the auto-generated stubs. For example VS2010 generates this methods:
[TestMethod()]
[HostType("ASP.NET")]
[AspNetDevelopmentServerHost("c:\\documents and settings\\ctremblay\\my documents\\visual studio 2010\\Projects\\WebApplication2\\WebApplication2", "/")]
[UrlToTest("http://localhost:3473/")]
public void testmeTest()
{
System.Diagnostics.Debugger.Break(); //FAILS here all the time
test target = new test(); // TODO: Initialize to an appropriate value
bool testbool = false; // TODO: Initialize to an appropriate value
bool expected = false; // TODO: Initialize to an appropriate value
bool actual;
actual = target.testme(testbool);
Assert.AreEqual(expected, actual);
Assert.Inconclusive("Verify the correctness of this test method.");
}
Based on MSDN http://msdn.microsoft.com/en-us/library/ms243172%28VS.80%29.aspx#DebuggingOnCassini
I should be able to add the System.Diagnostic method to debug within the IIS dev server but this fails on me everytime. This mean that debugging is impossible unless I remove the ASP/URL related annotations.
What do I have to gain using the annotations vs removing them completely?
I am attempting to create integration tests to make sure my views do not have any runtime errors in them. Thus I need to create a test that checks if ViewResult.ExecuteResult() works correctly but it seems I have hit a snag.
I found this site which gave me a starting point, and I have the following code:
[TestMethod]
public void RegisterResultExecutes()
{
//arrange
RequestContext requestContext = new RequestContext(new MockHttpContext(), new RouteData());
AccountController controller = new AccountController
{
FormsService = new MockFormsAuthenticationService(),
MembershipService = new MockMembershipService(),
Url = new UrlHelper(requestContext)
};
var result = controller.Register();
var sb = new StringBuilder();
Mock<HttpResponseBase> response = new Mock<HttpResponseBase>();
response.Setup(x => x.Write(It.IsAny<string>())).Callback<string>(y =>
{
sb.Append(y);
});
Mock<ControllerContext> controllerContext = new Mock<ControllerContext>();
controllerContext.Setup(x => x.HttpContext.Response).Returns(response.Object);
//act
result.ExecuteResult(controllerContext.Object);
}
The problem is that when result.ExecuteResult() is called I get the following exception
System.NullReferenceException: Object reference not set to an instance of an object.
System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context)
MyApp.Tests.Controllers.AccountControllerTest.RegisterResultExecutes() in C:\Users\KallDrexx\Documents\Projects\MyApp\MyApp.Tests\Controllers\AccountControllerTests.cs: line 297
Unfortunately, that stack trace isn't very useful as I'm not sure what it's trying to access that is null. Does anyone have any suggestions on how I can create a test for ExecuteResult()?
Based on the stack trace, it is something in the ViewResultBase.ExecuteResult method that throws the exception. Using reflector, here is the definition of that method:
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
if (string.IsNullOrEmpty(this.ViewName))
{
this.ViewName = context.RouteData.GetRequiredString("action");
}
ViewEngineResult result = null;
if (this.View == null)
{
result = this.FindView(context);
this.View = result.View;
}
TextWriter output = context.HttpContext.Response.Output;
ViewContext viewContext = new ViewContext(context, this.View, this.ViewData, this.TempData, output);
this.View.Render(viewContext, output);
if (result != null)
{
result.ViewEngine.ReleaseView(context, this.View);
}
}
Based on that code, an object reference exception could be thrown when the code tries to access the RouteData property from the context (if the name of the view wasn't explicitly given to the return type).
The exception could be thrown by accessing the HttpContext property. I haven't used Moq well enough to know if it can handle the fact that you haven't told it how to mock the HttpContext property, but you have told it how to mock the Response property from the HttpContext property's type, so that's another area that is suspect to me.
All other uses of context in the method are passing it into other methods, which if those were the problem, then the stack trace would have revealed that.
The easiest way to see which of the two I mentioned are the problem, I would write a quick test to pull those properties from your mocks and see which one causes the exception.
I hit the same problem as this just now and resolved it by setting the HttpContext.Current.
Try adding the following to your unit test code: e.g.
HttpContext.Current = new HttpContext(
new HttpRequest("", "http://mock", ""),
new HttpResponse(new StringWriter()));
One thing that I found useful for debugging this sort of problem rather than using reflector or ILSpy is to turn on debug symbols for the .Net framework code. That way you can attach to your NUnit process and see exactly what line of code is throwing the exception and therefore what you need to Mock in the test.
Shawn Burke has written an excellent blog article detailing how to set this up here: http://blogs.msdn.com/b/sburke/archive/2008/01/16/configuring-visual-studio-to-debug-net-framework-source-code.aspx