I found this code for doing a mock request for the purposes of unit testing. I'm trying to set the userhostaddress but I'm not clear how to use the same method outlined in this code to achieve that. I'm thinking it has to be done through reflection as I'm finding setting the headers is not allowed. Any ideas how I can achieve this?
public static HttpContext FakeHttpContext()
{
var httpRequest = new HttpRequest("", "http://fakurl/", "");
var stringWriter = new StringWriter();
var httpResponse = new HttpResponse(stringWriter);
var httpContext = new HttpContext(httpRequest, httpResponse);
var sessionContainer = new HttpSessionStateContainer("id", new SessionStateItemCollection(),
new HttpStaticObjectsCollection(), 10, true,
HttpCookieMode.AutoDetect,
SessionStateMode.InProc, false);
httpContext.Items["AspSession"] = typeof(HttpSessionState).GetConstructor(
BindingFlags.NonPublic | BindingFlags.Instance,
null, CallingConventions.Standard,
new[] { typeof(HttpSessionStateContainer) },
null)
.Invoke(new object[] { sessionContainer });
httpContext.Request.Headers["REMOTE_ADDR"] = "XXX.XXX.XXX.XXX"; // not allowed
return httpContext;
}
I also tried this mocking library:
https://gist.github.com/rally25rs/1578697
By the user host address is still readonly.
Best approach I've found is to have some sort of ContextService that implements an IContextService interface. This class/interface pair can do whatever operations you need it to. Point is, if you use a mocking framework in your unit tests like MOQ then you can wire up a mock context service to return a particular ip address.
This StackOverflow post has some good pointers: Moq: unit testing a method relying on HttpContext.
UPDATE:
The StackOverflow post you found is also a good one: How to set the IP (UserHostAddress) on a "mocked' BaseHttpContext?
I find that often I'll only need a few properties off of the context/request/response objects, so I often roll my own smaller variant:
public class ContextService : IContextService
{
public string GetUserHostAddress()
{
return HttpContext.Current.Request.UserHostAddress;
}
}
public interface IContextService
{
string GetUserHostAddress();
}
With that class/interface combo, I can then use Moq to wire up a fake service:
var contextMock = new Moq.Mock<IContextService>();
contextMock.Setup(c => c.GetUserHostAddress()).Returns("127.0.0.1");
Now every time I call contextMock.GetUserHostAddress(), I'll get "127.0.0.1". Rolling your own can be a great learning experience, especially if you don't need all the bells and whistles of a full-blown (or as full as possible anyway) HttpContext mock.
Related
I'm migrating a site over to use MVC 6. Currently I have tempdata store in cookies, but I can't find the set up of how to do this in the new MVC framework.
First, implement your ITempDataProvider. I did it this way, using JSON.Net.
public class CookieTempDataProvider : ITempDataProvider
{
readonly string CookieKey = "_tempdata";
public IDictionary<string,object> LoadTempData(HttpContext context)
{
var cookieValue = context.Request.Cookies[this.CookieKey];
if(string.IsNullOrWhiteSpace(cookieValue))
{
return new Dictionary<string, object>();
}
var decoded = Convert.FromBase64String(cookieValue);
var jsonAsString = Encoding.UTF8.GetString(decoded);
var dictionary = JsonConvert.DeserializeObject<IDictionary<string,object>>(jsonAsString, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All, TypeNameAssemblyFormat = FormatterAssemblyStyle.Full });
// The cookie really should be deleted when the SaveTempData() method is called with an empty dictionary
// but that does not seem to be working for some reason. Added this delete for now (maybe this is a beta issue)
// TODO: Revisit at next release
context.Response.Cookies.Delete(this.CookieKey);
return dictionary;
}
public void SaveTempData(HttpContext context, IDictionary<string,object> values)
{
if (values == null || values.Count == 0)
{
context.Response.OnStarting(() => Task.Run(() =>
{
context.Response.Cookies.Delete(this.CookieKey);
}));
return;
}
var jsonAsString = JsonConvert.SerializeObject(values, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All, TypeNameAssemblyFormat = FormatterAssemblyStyle.Full });
var bytes = Encoding.UTF8.GetBytes(jsonAsString);
var encoded = Convert.ToBase64String(bytes);
context.Response.Cookies.Append(this.CookieKey, encoded);
}
}
Next, in Startup.cs, where services are wired up, replace the default ITempDataProvider with your custom version, like so:
public void ConfigureServices(IServiceCollection services)
{
// Replace Temp Data Provider
var existing = services.FirstOrDefault(x => x.ServiceType == typeof(ITempDataProvider));
services.Remove(existing);
services.AddSingleton<ITempDataProvider, CookieTempDataProvider>();
}
EDIT
Since RC2 the original answer doesn't work any longer due to what seems like timing changes in the MVC request lifecycle...you'll receive an error about not being able to modify headers. I've updated the SaveTempData() method above to account for this.
I also had this need, so I've implemented a cookie-based TempData provider for ASP.NET Core MVC and published it on NuGet. It is available here.
If you think about TempData class for storing data for next request, there is some changes in MVC 6. You need to add additional package and configure it. Here are steps:
Remove "dnxcore50" from frameworks section in [project.json]. Session hasn't implementd yet in dnxcore50.
In the [project.json] add:
"Microsoft.AspNet.Session": "1.0.0-rc1-final"
Enable Caching and Session in class Startup.cs, method ConfigureServices, by adding next lines after services.AddMvc():
services.AddCaching();
services.AddSession();
Cinfigure it on class Startup.cs, method Configure, adding next line before app.UseMvc(...):
app.UseSession();
And that's it. But remember, you can store only primitive or serializable data types. If you need to store user defined data type, you need to serialized it. For that purpose we use "Newtonsoft.Json" lib. Here is example:
string json = JsonConvert.SerializeObject(myObject);
TempData["myKey"] = json;
I'm doing unittests for an asp.net application. Due to how the class I'm currently testing is designed it uses multiple threads and thus manually sets the language for one of the threads:
Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(HttpContext.Current.Request.UserLanguages[0]);
For this to work I'm mocking the httpcontext by using the following class in the unittests before I run the class that contains the above code:
public class HttpContextMock
{
public static HttpContext MockedHttpContext()
{
var httpRequest = new HttpRequest("", "MyUrl", "");
var stringWriter = new StringWriter();
var httpResponse = new HttpResponse(stringWriter);
var httpContext = new HttpContext(httpRequest, httpResponse);
var sessionContainer = new HttpSessionStateContainer("id", new SessionStateItemCollection(),
new HttpStaticObjectsCollection(), 10, true,
HttpCookieMode.AutoDetect,
SessionStateMode.InProc, false);
httpContext.Items["AspSession"] = typeof(HttpSessionState).GetConstructor(
BindingFlags.NonPublic | BindingFlags.Instance,
null, CallingConventions.Standard,
new[] { typeof(HttpSessionStateContainer) },
null)
.Invoke(new object[] { sessionContainer });
return httpContext;
}
Now the problem is even though I get a valid httpcontext the command
HttpContext.Current.Request.UserLanguages
Always returns null.
So my question is what can be done in order to ensure that the UserLanguages return at least 1 entry?
Mocking out HTTPContext isn't easy as you just found out.
Why not avoid that altogether?
Instead of
Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(HttpContext.Current.Request.UserLanguages[0]);
You could do something like
Thread.CurrentThread.CurrentUICulture = _ContextSpecificSettings.Language;
Where _ContextSpecificSettings is an instance of a class you code yourself. Could be something like:
class ContextSpecificSettings
{
HTTPContext _HTTPContext;
public ContextSpecificSettings()
{
_HTTPContext = HTTPContext;
}
public string Language // used string but you would of course use here whatever type is used in UserLanguages
{
get { return _HttpContext.Current.Request.UserLanguages[0]; }
}
}
Now, instead of trying to mock the HTTPContext, you only have to mock/stub your own ContextSpecificSettings class. Which is much easier to do because all you have to mock/stub is returning the language you want to use in your test.
All that is left to do is to ensure that the class containing that assignment to Thread.CurrentThread.CurrentUICulture gets its hands on an instance of your ContextSpecificSettings class. You can do that using simple constructor injection. For example:
class SomeThreadUsingClass
{
ContextSpecificSettings _Settings;
public SomeThreadContextUsingClass(ContextSpecificSettings useThisSettingsInstance)
{
_Settings = useThisSettingsInstance;
}
public Int SomeMethodUsingLanguage()
{
// <snip>
Thread.CurrentThread.CurrentUICulture = _Settings.Language;
// <snip>
return Whatever;
}
}
When you instantiate SomeThreadUsingClass in your actual code, you pass it a (new) instance of the ContextSpecificSettings class. When you instantiate SomeThreadUsingClass in a test, all you have to do is pass it an instance an ContextSpecificSettings Mock/Stub class.
By the way, this is the same technique as I described in Property.Settings.Default makes it hard to unit test any method that uses it
I have been experimenting with a lightweight solution for handling my business logic. It consists of a vanilla ADO.NET connection that is extended with Dapper, and monitored by Glimpse.ADO. The use case for this setup will be a web application that has to process a handful of queries asynchronously per request. Below a simple implementation of my setup in an MVC controller.
public class CatsAndDogsController : Controller
{
public async Task<ActionResult> Index()
{
var fetchCatsTask = FetchCats(42);
var fetchDogsTask = FetchDogs(true);
await Task.WhenAll(fetchCatsTask, fetchDogsTask);
ViewBag.Cats = fetchCatsTask.Result;
ViewBag.Dogs = fetchDogsTask.Result;
return View();
}
public async Task<IEnumerable<Cat>> FetchCats(int breedId)
{
IEnumerable<Cat> result = null;
using (var connection = CreateAdoConnection())
{
await connection.OpenAsync();
result = await connection.QueryAsync<Cat>("SELECT * FROM Cat WHERE BreedId = #bid;", new { bid = breedId });
connection.Close();
}
return result;
}
public async Task<IEnumerable<Dog>> FetchDogs(bool isMale)
{
IEnumerable<Dog> result = null;
using (var connection = CreateAdoConnection())
{
await connection.OpenAsync();
result = await connection.QueryAsync<Dog>("SELECT * FROM Dog WHERE IsMale = #im;", new { im = isMale });
connection.Close();
}
return result;
}
public System.Data.Common.DbConnection CreateAdoConnection()
{
var sqlClientProviderFactory = System.Data.Common.DbProviderFactories.GetFactory("System.Data.SqlClient");
var dbConnection = sqlClientProviderFactory.CreateConnection();
dbConnection.ConnectionString = "SomeConnectionStringToAwesomeData";
return dbConnection;
}
}
I have some questions concerning the creation of the connection in the CreateAdoConnection() method. I assume the following is happening behind the scenes.
The call to sqlClientProviderFactory.CreateConnection() returns an instance of System.Data.SqlClient.SqlConnection passed as a System.Data.Common.DbConnection. At this point Glimpse.ADO.AlternateType.GlimpseDbProviderFactory kicks in and wraps this connection in an instance of Glimpse.Ado.AlternateType.GlimpseDbConnection, which is also passed as a System.Data.Common.DbConnection. Finally, this connection is indirectly extended by the Dapper library with its query methods, among them the QueryAsync<>() method used to fetch the cats and dogs.
The questions:
Is the above assumption correct?
If I use Dapper's async methods with this connection - or create a System.Data.Common.DbCommand with this connection's CreateCommand() method, and use it's async methods - will those calls internally always end up using the vanilla async implementations of these methods as Microsoft has written them for System.Data.SqlClient.SqlConnection and System.Data.SqlClient.SqlCommand? And not some other implementations of these methods that are actually blocking?
How much perf do I lose with this setup compared to just returning a new System.Data.SqlClient.SqlConnection directly? (So, without the Glimpse.ADO wrapper)
Any suggestions on improving this setup?
Yes pretty much. GlimpseDbProviderFactory wraps/decorates/proxies all the registered factories. We then pass any calls we get through to the factory we wrap (in this case SQL Server). In the case of CreateConnection() we ask the inner factory we have, to create a connection, when we get that connection, we wrap it and then return it to the originating caller
Yes. Glimpse doesn't turn what was an async request into a blocking request. We persevere the async chain all the way though. If you are interested, the code in question is here.
Very little. In essence, using a decorator pattern like this adds only one or two frames to the call stack. Compared to most operations performed during the request lifecycle, the time to observe whats happening here is extremely minimal.
What you have looks great. Only suggestion is to maybe us this code to build the factory. This code means that you can shift your connection string, etc to the web.config.
i try to unit test an EntitySetController. I can test Get but have problems in testing the Post Method.
I played around with SetODataPath and SetODataRouteName but when i call this.sut.Post(entity) i get a lot of errors regarding missing Location Header, missing OData-Path, missing Routes.
I am at my wit's end.
Is there anybody out there who has successfully testet their EntitySetController?
Has anybody an idea for me?
Maybe should i test only the protected overrided methods from my EntitySetController implementation? But how can i test protected methods?
Thanks for your help
Came here looking for a solution aswell. This seems to work however not sure if there is a better way.
The controller needs a minimum of CreateEntity and GetKey overrides:
public class MyController : EntitySetController<MyEntity, int>
{
protected override MyEntity CreateEntity(MyEntity entity)
{
return entity;
}
protected override int GetKey(MyEntity entity)
{
return entity.Id;
}
}
Where MyEntity is really simple:
public class MyEntity
{
public int Id { get; set; }
public string Name { get; set; }
}
Looks like you need at least:
+ Request with a URI
+ 3 keys in the request header, MS_HttpConfiguration, MS_ODataPath and MS_ODataRouteName
+ A HTTP configuration with a route
[TestMethod]
public void CanPostToODataController()
{
var controller = new MyController();
var config = new HttpConfiguration();
var request = new HttpRequestMessage();
config.Routes.Add("mynameisbob", new MockRoute());
request.RequestUri = new Uri("http://www.thisisannoying.com/MyEntity");
request.Properties.Add("MS_HttpConfiguration", config);
request.Properties.Add("MS_ODataPath", new ODataPath(new EntitySetPathSegment("MyEntity")));
request.Properties.Add("MS_ODataRouteName", "mynameisbob");
controller.Request = request;
var response = controller.Post(new MyEntity());
Assert.IsNotNull(response);
Assert.IsTrue(response.IsSuccessStatusCode);
Assert.AreEqual(HttpStatusCode.Created, response.StatusCode);
}
I'm not too sure about the IHttpRoute, in the aspnet source code (I had to link to this to figure this all out) the tests use mocks of this interface. So for this test I just create a mock of this and implement the RouteTemplate property and GetVirtualPath method. All the others on the interface were not used during the test.
public class MockRoute : IHttpRoute
{
public string RouteTemplate
{
get { return ""; }
}
public IHttpVirtualPathData GetVirtualPath(HttpRequestMessage request, IDictionary<string, object> values)
{
return new HttpVirtualPathData(this, "www.thisisannoying.com");
}
// implement the other methods but they are not needed for the test above
}
This is working for me however I am really not too sure about the ODataPath and IHttpRoute and how to set it correctly.
In addition to the answer from #mynameisbob, I have found you also may need to set the HttpRequestContext as well on the Request properties:
var requestContext = new HttpRequestContext();
requestContext.Configuration = config;
request.Properties.Add(HttpPropertyKeys.RequestContextKey, requestContext);
I needed the above additions for example when creating an HttpResponseMessage as follows:
public virtual HttpResponseException NotFound(HttpRequestMessage request)
{
return new HttpResponseException(
request.CreateResponse(
HttpStatusCode.NotFound,
new ODataError
{
Message = "The entity was not found.",
MessageLanguage = "en-US",
ErrorCode = "Entity Not Found."
}
)
);
}
Without having the HttpRequestContext set, the above method will throw an Argument Null Exception as the CreateResponse extension method attempts to get the HttpConfiguration from the HttpRequestContext (rather than directly from the HttpRequest).
OK updated answer.
I've also found to support executing a returned IHttpActionResult successfully, a few more things are needed.
Here is the best approach I found so far, I'm sure there is a better way but this works for me:
// Register OData configuration with HTTP Configuration object
// Create an ODataConfig or similar class in App_Start
ODataConfig.Register(config);
// Get OData Parameters - suggest exposing a public GetEdmModel in ODataConfig
IEdmModel model = ODataConfig.GetEdmModel();
IEdmEntitySet edmEntitySet = model.EntityContainers().Single().FindEntitySet("Orders");
ODataPath path = new ODataPath(new EntitySetPathSegment(edmEntitySet));
// OData Routing Convention Configuration
var routingConventions = ODataRoutingConventions.CreateDefault();
// Attach HTTP configuration to HttpRequestContext
requestContext.Configuration = config;
// Attach Request URI
request.RequestUri = requestUri;
// Attach Request Properties
request.Properties.Add(HttpPropertyKeys.HttpConfigurationKey, config);
request.Properties.Add(HttpPropertyKeys.RequestContextKey, requestContext);
request.Properties.Add("MS_ODataPath", path);
request.Properties.Add("MS_ODataRouteName", "ODataRoute");
request.Properties.Add("MS_EdmModel", model);
request.Properties.Add("MS_ODataRoutingConventions", routingConventions);
request.Properties.Add("MS_ODataPathHandler", new DefaultODataPathHandler());
Also, to get the correct Location header values etc, you really want to call your Web Api application OData configuration code.
So rather than using:
config.Routes.Add("mynameisbob", new MockRoute());
You should separate the portion of the WebApiConfig class that sets up your OData routes into a separate class (e.g. ODataConfig) and use that to register the correct routes for your tests:
e.g.
ODataConfig.Register(config);
The only things you then have to watch out for is that the following lines match your routing configuration:
request.Properties.Add("MS_ODataPath", new ODataPath(new EntitySetPathSegment("MyEntity")));
request.Properties.Add("MS_ODataRouteName", "mynameisbob");
So if your Web API OData configuration is as follows:
config.Routes.MapODataRoute("ODataRoute", "odata", GetEdmModel());
private static IEdmModel GetEdmModel()
{
ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<MyEntity>("MyEntities");
IEdmModel model = modelBuilder.GetEdmModel();
return model;
}
Then this is the correct configuration:
request.Properties.Add("MS_ODataPath", new ODataPath(new EntitySetPathSegment("MyEntities")));
request.Properties.Add("MS_ODataRouteName", "ODataRoute");
With this in place, your Location header will be generated correctly.
In addition to everything here, I had to manually attach the context to the request, as well as create route data. Unfortunately there is no way I found to unit-test without a dependency on route/model configuration.
So using a route called "ODataRoute" which is all part of the normal configuration established in my static ODataConfig.Configure() method (same as above, it creates the model and calls a bunch of MapODataServiceRoute), the following code works to prepare a controller for a test:
protected static void SetupControllerForTests(ODataController controller,
string entitySetName, HttpMethod httpMethod)
{
//perform "normal" server configuration
var config = new HttpConfiguration();
ODataConfig.Configure(config);
//set up the request
var request = new HttpRequestMessage(httpMethod,
new Uri(string.Format("http://localhost/odata/{0}", entitySetName)));
//attach it to the controller
//note that this will also automagically attach a context to the request!
controller.Request = request;
//get the "ODataRoute" route from the configuration
var route = (ODataRoute)config.Routes["ODataRoute"];
//extract the model from the route and create a path
var model = route.PathRouteConstraint.EdmModel;
var edmEntitySet = model.FindDeclaredEntitySet(entitySetName);
var path = new ODataPath(new EntitySetPathSegment(edmEntitySet));
//get a couple more important bits to set in the request
var routingConventions = route.PathRouteConstraint.RoutingConventions;
var pathHandler = route.Handler;
//set the properties of the request
request.SetConfiguration(config);
request.Properties.Add("MS_ODataPath", path);
request.Properties.Add("MS_ODataRouteName", "ODataRoute");
request.Properties.Add("MS_EdmModel", model);
request.Properties.Add("MS_ODataRoutingConventions", routingConventions);
request.Properties.Add("MS_ODataPathHandler", pathHandler);
//set the configuration in the request context
var requestContext = (HttpRequestContext)request.Properties[HttpPropertyKeys.RequestContextKey];
requestContext.Configuration = config;
//get default route data based on the generated URL and add it to the request
var routeData = route.GetRouteData("/", request);
request.SetRouteData(routeData);
}
This took me the better part of a few days to piece together, so I hope this at least saves someone else the same.
I'm trying to use WebRequest.RegisterPrefix to register a decorator IWebRequestCreate implementation with the intention being to add "debug" scenarios (like emulating different connectivity scenarios).
I'm using the Mango beta 2 SDK and the RegisterPrefix method always returns true when used with "http://" as a prefix (or "http" for that matter), but the registered IWebRequestCreate instance is not being used.
I can see from the documentation that it should return false for duplicates, but it doesn't seem to be functioning as documented.
Is there any other way of achieving what I'm after in a way that is transparent to consumers?
I'm using WebRequest.RegisterPrefix for unit testing, registering an IWebRequestCreate implementation for a prefix of test://, and this does work.
I found that after registering an IWebRequestCreate for http://, calling WebRequest.Create with an http:// uri would return a request created from the registered IWebRequestCreate, but calling WebRequest.CreateHttp would still return an HttpWebRequest.
The following code should verify this, and I'm using the Mango Beta 2 SDK (6-29-11):
public partial class MainPage : PhoneApplicationPage
{
public class FakeRequest : WebRequest
{
private Uri _uri;
public FakeRequest(Uri uri)
{
_uri = uri;
}
public override Uri RequestUri { get { return _uri; } }
}
public class FakeRequestFactory : IWebRequestCreate
{
public WebRequest Create(Uri uri)
{
return new FakeRequest(uri);
}
}
// Constructor
public MainPage()
{
InitializeComponent();
// returns System.Net.Browser.ClientHttpWebRequest
var request1 = WebRequest.Create("http://www.foo.com");
// returns System.Net.Browser.ClientHttpWebRequest
var request2 = WebRequest.CreateHttp("http://www.foo.com");
// returns true
bool result1 = WebRequest.RegisterPrefix("http://", new FakeRequestFactory());
// returns FakeRequest
var request3 = WebRequest.Create("http://www.foo.com");
// returns System.Net.Browser.ClientHttpWebRequest
var request4 = WebRequest.CreateHttp("http://www.foo.com");
// returns false
bool result2 = WebRequest.RegisterPrefix("http://", new FakeRequestFactory());
// returns false, as per the note in the documention
bool result3 = HttpWebRequest.RegisterPrefix("http://", new FakeRequestFactory());
}
}
Hey I know this question is 2 years old but I came across the same problem. I think you'll find that WebRequest.RegisterPrefix() does return false if you try to register http: (notice the single colon, no forward slashes). If I ever find a workaround, I'll try to remember to update this post.
EDIT
In my particular case I wanted to throw out System.Net.FtpWebRequest and roll my own FTP client implementation (because the framework's implementation sucks).
In order to do that, I used reflection (and a bunch of late binding tricks) to get the arraylist of registered prefix and remove the ones that are linked to the internal System.Net.FtpWebRequestCreator class.
I'm not sure if all of these APIs are available for windows phone, but here's what I did:
Type webRequest = typeof(System.Net.WebRequest);
Assembly system = Assembly.GetAssembly(webRequest);
Type ftpWebRequestCreator = system.GetType("System.Net.FtpWebRequestCreator");
ArrayList prefixList = (ArrayList)webRequest.GetProperty("PrefixList", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null, null);
IEnumerator enumerator = prefixList.GetEnumerator();
while (enumerator != null && enumerator.MoveNext()) {
if (object.ReferenceEquals(enumerator.Current.Creator.GetType(), ftpWebRequestCreator)) {
prefixList.Remove(enumerator.Current);
if (System.Net.WebRequest.RegisterPrefix(enumerator.Current.Prefix, new CustomWebRequestCreator())) {
enumerator = null;
} else {
enumerator = prefixList.GetEnumerator();
}
}
}
// Now I can use Create() on the base class
System.Net.WebRequest myCustomWebRequest = System.Net.WebRequest.Create("ftp://example.com/public");
This works by finding all prefixes that are registered with FtpWebRequestCreator and replacing them with my own creator. It should be fairly straightforward to adapt this for http(s).
The phone only has a client stack so RegisterPrefix has no effect on the phone.