How to mock Server Variables using NSubstitute - c#

I have the following method to get a user's IP address:
public string GetUserIpAddress(HttpContext context)
{
var ipAddress = context.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
if (!string.IsNullOrEmpty(ipAddress))
{
string[] addresses = ipAddress.Split(',');
if (addresses.Length != 0)
{
return addresses[0];
}
}
return context.Request.ServerVariables["REMOTE_ADDR"];
}
I am trying to write a unit test for this but am struggling to do so (using NSubstitute and NUnit). I have read other articles and so far this is my attempt:
var request = Substitute.For<HttpRequestBase>();
request.ServerVariables.Returns(new System.Collections.Specialized.NameValueCollection
{
{ "HTTP_X_FORWARDED_FOR", "value here" }
});
var httpContext = Substitute.For<HttpContextBase>();
httpContext.Request.Returns(request);
var helper = new UserIpAddressHelper();
var result = helper.GetUserIpAddress(httpContext.ApplicationInstance.Context);
The error is that the result is returning null when I want it to be configured properly with the server variables above.

HttpRequestBase has been created specifically to be testable, because it is abstract and can therefore be overridden by a mocking framework.
If you want GetUserIpAddress to be testable, have it accept a HttpContextBase instance:
public string GetUserIpAddress(HttpContextBase context)
If it needs to be called from code that uses HttpContext that can be wrapped:
HttpContext context;
var ipAddress = GetUserIpAddress(new HttpContextWrapper(context));

Related

Test all endpoints for authentication, .Net core 3.0

I want to build integration tests to make sure all of our endpoints are locked behind authentication.
I will then fetch all of our endpoints from our swagger.
How can i await this call and then use this data as memberData or classData? Since it's async.
Should i use fixtures, or some kind of ----Data?
[Collection("A collection")]
public class EndpointsTests
{
RouteDataFixture fixture;
public EndpointsTests(RouteDataFixture fixture)
{
this.fixture = fixture;
}
[Theory]
[ClassData(typeof(SomeClassWithAsyncConstructor))]
public async Task Test_This(string path, string method)
{
//test the awaited data from class
if(method == "GET"=
var response = await fixture.GetAsync(path)
//Check if the response is unauthorized since we didn't send a token
}
}
If there is no special reason you can use unit test instead of integration test and you can check your endpoints defined or not defined any authorization attribute. For example;
public class SomeController : Controller
{
[AllowAnonymous]
public IAsyncResult Post1()
{
// codes...
}
[Authorize("some_permission")]
public IAsyncResult Post2()
{
// codes...
}
public IAsyncResult Post3()
{
// codes...
}
}
this is your controller class.
[Fact]
public void Test()
{
var _endpoints = new List<(Type, MethodInfo)>(); // All endpoints in my project
var asm = Assembly.Load("MyAssembly");
var cType = typeof(Controller);
var types = asm.GetTypes().Where(x => x.IsSubclassOf(cType)).ToList();
foreach (Type t in types)
{
var mInfos = t.GetMethods(BindingFlags.Public | BindingFlags.Instance).Where(x => x.DeclaringType.Equals(t)).ToList();
foreach (MethodInfo mInfo in mInfos)
_endpoints.Add((t, mInfo));
}
var nonAuthEndPoints = _endpoints.Where(x => !x.IsDefined(typeof(AuthorizeAttribute)) && !x.IsDefined(typeof(AllowAnonymousAttribute)));
nonAuthEndPoints.Should().BeEmpty();
}
and this is your test method. This would check all endpoints and force they should have AllowAnonymous or Authorize.
In this example your Post1 and Post2 endpoints passed the test but post3 failed.

Mock a method from another class called from a mocked method

We are creating unit tests for an application and ran into a problem creating certain tests.
We are unit testing the following Handle() method of the class ActivateCommandHandler:
public class ActivateCommand : IRequest<HttpResponseMessage>
{
public string Controller { get; set; }
public ActivateCommand(string controllername)
{
this.Controller = controllername;
}
}
public class ActivateCommandHandler : CommandHandler<ActivateCommand, HttpResponseMessage>
{
protected readonly ICommandsGateway _commandsGateway;
protected readonly EndpointSettings _endpoints;
protected readonly IUserProfile _userprofile;
public ActivateCommandHandler(IMediator mediator, ICommandsGateway commandsGateway, IOptions<EndpointSettings> endpoints, IValidationContext validationContext, IUserProfile currentUser) : base(mediator, validationContext, currentUser)
{
_commandsGateway = commandsGateway;
_endpoints = endpoints.Value;
_userprofile = currentUser;
}
public override async Task<HttpResponseMessage> Handle(ActivateCommand command, CancellationToken cancellationToken)
{
if (_endpoints.EndpointExists(command.Controller))
{
// Check whether the second server controller is deactivated
string peercontroller = _endpoints.GetPeerController(command.Controller);
if (!string.IsNullOrEmpty(peercontroller))
{
BaseRedundancySwitchStatus controllerStatus = await _commandsGateway.GetRedundancySwitchStatus(_endpoints.GetEndpointAddress(peercontroller));
if ((controllerStatus.CurrentState == "Activated") || (controllerStatus.CurrentState == "ActivatedWithWarning") || (controllerStatus.CurrentState == "Activating"))
{
var resp = new HttpResponseMessage(HttpStatusCode.Conflict)
{
Content = new StringContent($"{peercontroller},{controllerStatus.CurrentState}")
};
return resp;
}
}
var result = await _commandsGateway.PostActivateCommand(_endpoints.GetEndpointAddress(command.Controller));
return result;
}
else
{
throw new InvalidControllerNameException($"ERROR: The controller {command.Controller} does not exist as an endpoint on this Control Center!");
}
}
}
For this the following were mocked: _endpoints, command and _commandsGateway (interface). This works great for unit testing the parameter validation. But we now want to test the behaviour when the peercontroller status is set to a specific value.
To do this we are trying to mock out the function _commandsGateway.GetRedundancySwitchStatus(). The following is the actual test implementation. We mock the _commandsGateway.GetRedundancySwitchStatus() function to return the expected BaseRedundancySwitchStatus with CurrentState set to "Activated". After that we call the handler of the actual function to be tested and check whether we get the expected error.
[Fact]
public async void ShouldHaveErrors_PeerControllerStateActivated()
{
var command = new ActivateCommand("Server Controller Slave1");
BaseRedundancySwitchStatus result = new BaseRedundancySwitchStatus()
{
CurrentState = "Activated"
};
_commandsGateway
.Setup(s => s.GetRedundancySwitchStatus("Server Controller Slave1"))
.ReturnsAsync(result);
HttpResponseMessage res = await _handler.Handle(command, CancellationToken.None);
Assert.True(res.StatusCode == System.Net.HttpStatusCode.Conflict);
}
Debugging the code, when I step through the code in the Handle() method where _commandsGateway.GetRedundancySwitchStatus is called, I can see that _endpoints.GetEndpointAddress(command.Controller) (which is the parameter) is called and the correct value is returned. After this the debugger steps to the next line without any indication of having executed the mock GetRedundancySwitchStatus() function. Inspecting the controllerStatus variable the value is null. I would expect the value to be the BaseRedundancySwitchStatus object which is supposed to be returned by the mocked GetRedundancySwitchStatus() function.
Where are we going wrong?

.NET Core: How to get a `Controller` instance (or just it's full name) from ViewLocationExpanderContext

In MVC5, I had my own VirtualPathProviderViewEngine and had the following:
string controllerAssemblyName = controllerContext.Controller.GetType().Assembly.FullName;
I used that for dynamically adding view locations.
Anyway, I am migrating to .NET Core and writing my own IViewLocationExpander and need to determine the controller type there to do the same thing. The method signature is as follows:
public virtual IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations)
All I have is an instance of ViewLocationExpanderContext, which only provides ControllerName and AreaName properties, but no actual instance of a Controller. Is there a way to get an instance or at least full type name of the controller using those 2 properties?
I also tried the following:
var controllerContext = new ControllerContext(context.ActionContext);`
That gives me an instance of ControllerContext, but unlike MVC5, it doesn't have a Controller property on it.
Inspect the ActionContext to access the desired information.
Should be able to drill down into the context to get the action controller type info and assembly full name
public virtual IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations) {
var controllerActionDescriptor = context.ActionContext.ActionDescriptor as ControllerActionDescriptor;
if(controllerActionDescriptor != null) {
var controllerTypeInfo = controllerActionDescriptor.ControllerTypeInfo;
//From the type info you should be able to get the assembly
var controllerAssemblyName = controllerTypeInfo.AsType().Assembly.FullName.ToString();
}
//...
}
Thanks to #Nkosi. I have used his answer and my solution is as follows. I am not sure if the CreateControllerFactory() part is good, but it's my fallback in case controllerActionDescriptor is NULL:
string controllerAssemblyName = null;
var controllerActionDescriptor = context.ActionContext.ActionDescriptor as ControllerActionDescriptor;
if (controllerActionDescriptor != null)
{
var controllerTypeInfo = controllerActionDescriptor.ControllerTypeInfo;
controllerAssemblyName = controllerTypeInfo.AsType().Assembly.FullName;
}
else
{
var controllerContext = new ControllerContext(context.ActionContext);
var factory = CreateControllerFactory();
var controller = factory.CreateController(controllerContext);
controllerAssemblyName = controller.GetType().Assembly.FullName;
}
private static DefaultControllerFactory CreateControllerFactory()
{
var propertyActivators = new IControllerPropertyActivator[]
{
new DefaultControllerPropertyActivator(),
};
return new DefaultControllerFactory(
new DefaultControllerActivator(new TypeActivatorCache()),
propertyActivators);
}

How to mock external dependencies using JustMock Lite

I am using free version of JustMock to mock some external dependencies like GetUserSettingsResponse which is a sealed class. The problem is that the free version does not allow us to mock sealed classes. I cannot use the full version because of some reasons. Below is my code sample:
public GetUserSettingsResponse GetUserSettingsResponse(string emailAddress)
{
var response = service.GetUserSettings(
emailAddress,
UserSettingName.ExternalEwsUrl,
UserSettingName.InternalEwsUrl,
UserSettingName.EwsSupportedSchemas);
return response;
}
This is the method I am trying to mock as below:
[TestMethod]
public void GetExchangeService()
{
//Arrange
string url = "";
GetUserSettingsResponse response;
ObjectOfMyClass.Arrange(x => x.GetUserSettingsResponse(Arg.IsAny<string>())).Returns(new GetUserSettingsResponse()); //this is where it throws exception saying that only non-sealed classes can be mocked with lite version of just mock
}
Edit: My application is a Web service which is basically using EWS managed APIs to get the email account details from the exchange server. So, in order to start the communication I am first doing the AutoDiscovery to get the url out of email address of the user. So the subject under test is below method which internally calls GetUserSettingsResponse method:
public class MyClass
{
public ExchangeService GetExchangeService(
string userName,
string password,
int version,
string emailAddress,
string userId)
{
AutoDiscoverService service = new AutodiscoverService();
service.UseDefaultCredentials = false;
service.Credentials = new NetworkCredential(userName, password);
service.EnableScpLookup = true;
service.ReturnClientRequestId = true;
service.PreAuthenticate = false;
service.RedirectionUrlValidationCallback = RedirectionUrlValidationCallback;
var url = string.Empty;
var response = GetUserSettingsResponse(emailAddress);
if (response.TryGetSettingValue(UserSettingName.ExternalEwsUrl, out settingValue)
|| response.TryGetSettingValue(UserSettingName.InternalEwsUrl, out settingValue))
{
ewsurl = settingValue;
}
var exchangeService = new ExchangeService((ExchangeVersion)version)
{
Credentials = new WebCredentials(userName, password),
KeepAlive = true,
Url = new Uri(ewsurl)
};
return exchangeService;
}
}

HttpContext is null in a method called inside Parallel.For

Posting this question after trying a lot. Doing normal for is not an option because we need to do a large amount of processing in very less time.
I have GetDataFor() inside which HttpContext.Current is used.
The code looks like this:
public void SomeMethod()
{
var context = HttpContext.Current;
Parallel.For(0, 100, i =>
{
var data = GetDataFor(i, context);
});
}
public data GetDataFor(int i, HttpContext context)
{
Uri requestUri = null;
if (HttpContext.Current != null)
{
requestUri = HttpContext.Current.Request.Url;
sCookie = string.Format("{0}", HttpContext.Current.Request.Headers["cookie"]);
}
else
{
requestUri = context.Request.Url;
}
//do something
return data;
}
Everything works fine inside normal for loop. However, when I call it inside Parallel.For and pass HttpContext.Current, HttpContext.Current.Request, HttpContext.Current.Request.Url as method parameters:
HttpContext.Current cannot be serialized because it does not have a parameterless constructor
Passing HttpContextBase httpContext = null as parameter throws:
To be XML serializable, types which inherit from ICollection must have an implementation of Add(System.Object) at all levels of their inheritance hierarchy. System.Web.HttpApplicationStateBase does not implement Add(System.Object).
Tried making a property:
public string[] httpContextData
{
get
{
string requestUrl = HttpContext.Current.Request.Url.ToString();
string sCookie = string.Format("{0}", HttpContext.Current.Request.Headers["cookie"]);
return new string[] { requestUrl, sCookie };
}
}
and using in method:
var contextData = httpContextData;
which throws:
System.Uri cannot be serialized because it does not have a parameterless constructor
I did all this to send it's reference and state but unable to understand why the problem is not solving.
How do I use HttpContext.Current inside Parallel.For? What am I doing wrong here?
Btw, the needed stuff are:
HttpContext.Current.Request.Url and HttpContext.Current.Request.Headers["cookie"]
HttpContext.Current is only available (not null) inside request-handling threads. Parallel.For creates multiple threads, none of which is has access to HttpContext.Current.
You have to pass all data that code in Parallel.For threads needs either through
local variables assigned before the loop or
TLocal instance used in Parallel.For<TLocal>.
In any event, code such as HttpContext.Current.... is out.
The code is proprietary hence I'm only posting the relevant parts:
Since passing the following objects:
HttpContext.Current
HttpContext.Current.Request
HttpContext.Current.Request.Url
as params to GetDataFor was throwing so many errors.
Also, my needs were only
request url which can be re-generated by passing url as string to it's constructor
and a request header's value which is essentially a string
I only passed string to GetDataFor() method:
public void SomeMethod()
{
string requestUrl = HttpContext.Current.Request.Url.ToString();
string sCookie = string.Format("{0}", HttpContext.Current.Request.Headers["cookie"]);
Parallel.For(0, 100, i =>
{
var data = GetDataFor(i,
requestUrl: requestUrl,
sCookie: sCookie);
});
}
public data GetDataFor(int i, string requestUrl = null, string sCookie = null)
{
Uri requestUri = null;
if (HttpContext.Current != null)
{
requestUri = HttpContext.Current.Request.Url;
sCookie = string.Format("{0}", HttpContext.Current.Request.Headers["cookie"]);
}
else
{
requestUri = new Uri(requestUrl);
}
//do something
return data;
}

Categories