I'm using ServiceStack / StructureMap / Moq. The service makes a call to Session, which is type ServiceStack.CacheAccess.ISession. For unit tests, I created a Mock object using Moq, and added it to the StructureMap configuration:
protected Mock<ISession> sessionMock = new Mock<ISession>();
ObjectFactory.Configure(
cfg =>
{
cfg.For<ISession>().Use(sessionMock.Object);
However, I was not surprised when the Session object was null -- I'm pretty sure I'm leaving out a step. What else do I need to do to fill my Session property with a mock object?
[EDIT] Here's a simple test scenario
Code to test. Simple request / service
[Route("getKey/{key}")]
public class MyRequest:IReturn<string>
{
public string Key { get; set; }
}
public class MyService:Service
{
public string Get(MyRequest request)
{
return (string) Session[request.Key];
}
}
The base test class and MockSession classes
// test base class
public abstract class MyTestBase : TestBase
{
protected IRestClient Client { get; set; }
protected override void Configure(Container container)
{
// this code is never reached under any of my scenarios below
container.Adapter = new StructureMapContainerAdapter();
ObjectFactory.Initialize(
cfg =>
{
cfg.For<ISession>().Singleton().Use<MockSession>();
});
}
}
public class MockSession : ISession
{
private Dictionary<string, object> m_SessionStorage = new Dictionary<string, object>();
public void Set<T>(string key, T value)
{
m_SessionStorage[key] = value;
}
public T Get<T>(string key)
{
return (T)m_SessionStorage[key];
}
public object this[string key]
{
get { return m_SessionStorage[key]; }
set { m_SessionStorage[key] = value; }
}
}
And tests. See comments for where I'm seeing the failure. I didn't really expect versions 1 & 2 to work, but hoped version 3 would.
[TestFixture]
public class When_getting_a_session_value:MyTestBase
{
[Test]
public void Test_version_1()
{
var session = ObjectFactory.GetInstance<MockSession>();
session["key1"] = "Test";
var request = new MyRequest {Key = "key1"};
var client = new MyService(); // generally works fine, except for things like Session
var result = client.Get(request); // throws NRE inside MyService
result.ShouldEqual("Test");
}
[Test]
public void Test_version_2()
{
var session = ObjectFactory.GetInstance<MockSession>();
session["key1"] = "Test";
var request = new MyRequest {Key = "key1"};
var client = ObjectFactory.GetInstance<MyService>();
var result = client.Get(request); // throws NRE inside MyService
result.ShouldEqual("Test");
}
[Test]
public void Test_version_3()
{
var session = ObjectFactory.GetInstance<MockSession>();
session["key1"] = "Test";
var request = new MyRequest {Key = "key1"};
var client = CreateNewRestClient();
var result = client.Get(request); // throws NotImplementedException here
result.ShouldEqual("Test");
}
}
It looks like you're trying to create unit tests, but you're using an AppHost like you wound an Integration test. See this previous answer for differences between the two and docs on Testing.
You can mock the Session by registering an instance in Request.Items[Keywords.Session], e.g:
[Test]
public void Can_mock_IntegrationTest_Session_with_Request()
{
using var appHost = new BasicAppHost(typeof(MyService).Assembly).Init();
var req = new MockHttpRequest();
req.Items[Keywords.Session] = new AuthUserSession {
UserName = "Mocked"
};
using var service = HostContext.ResolveService<MyService>(req);
Assert.That(service.GetSession().UserName, Is.EqualTo("Mocked"));
}
Otherwise if you set AppHost.TestMode=true ServiceStack will return the IAuthSession that's registered in your IOC, e.g:
[Test]
public void Can_mock_UnitTest_Session_with_IOC()
{
using var appHost = new BasicAppHost
{
TestMode = true,
ConfigureContainer = container =>
{
container.Register<IAuthSession>(c => new AuthUserSession {
UserName = "Mocked",
});
}
}.Init();
var service = new MyService {
Request = new MockHttpRequest()
};
Assert.That(service.GetSession().UserName, Is.EqualTo("Mocked"));
}
Related
I am writing a Razor Pages app for a University project, which I am required to test. I couldn't find many sources and examples online on testing Razor Pages and I'm trying to follow the examples on this link : https://learn.microsoft.com/en-us/aspnet/core/testing/razor-pages-testing?view=aspnetcore-2.1
My first problem is unit testing:
This is the test method I wrote, it's supposed to check that a value that is filled in the OnGet method on my model is receiving the correct value:
[Fact]
public void OnGet_ViewStores()
{
// Arrange
var httpContext = new DefaultHttpContext();
var modelState = new ModelStateDictionary();
var actionContext = new ActionContext(httpContext, new RouteData(), new PageActionDescriptor(), modelState);
var modelMetadataProvider = new EmptyModelMetadataProvider();
var viewData = new ViewDataDictionary(modelMetadataProvider, modelState);
var pageContext = new PageContext(actionContext)
{
ViewData = viewData
};
var storesModel = new StoresModel()
{
PageContext = pageContext,
Url = new UrlHelper(actionContext)
};
#region snippet2
// Act
storesModel.OnGet();
#endregion
#region snippet3
// Assert
var actualStores = wsep1.Services.ViewAllStores(1);
Assert.Equal(storesModel.StoreDetails, actualStores);
#endregion
}
And this is the model which is being checked:
public class StoresModel : PageModel
{
public List<string> StoreDetails { get; set; }
public string Message { get; set; }
public int clientId;
public void OnGet()
{
clientId = (int)HttpContext.Session.GetInt32("clientId");
Message = "Your clientId id is " + clientId;
StoreDetails = wsep1.Services.ViewAllStores(clientId);
}
}
The problem is that the test throws an exception because I am using an HttpContext.Session which is not configures properly in the test. In my real project it is configured beforehand in Startup.cs in this method:
public void ConfigureServices(IServiceCollection services)
{
services.AddWebSocketManager();
services.AddMvc();
services.AddDistributedMemoryCache();
services.AddTransient<ShoppingHandler>();
services.AddSession(options =>
{
// Set a short timeout for easy testing.
options.IdleTimeout = TimeSpan.FromSeconds(1000);
options.Cookie.HttpOnly = true;
});
}
But I can't seem to find a way to configure this in my test.
My second problem is with integration testing:
I am trying to run a very basic test with the Test Server, this is my test class:
public class IndexPageTest : IClassFixture<TestFixture<Client.Startup>>
{
private readonly HttpClient _client;
public IndexPageTest(TestFixture<Client.Startup> fixture)
{
_client = fixture.Client;
}
#region snippet1
[Fact]
public async Task Request_ReturnsSuccess()
{
// Act
var response = await _client.GetAsync("/");
// Assert
response.EnsureSuccessStatusCode();
}
#endregion
}
I hardly changed the TextFixture class that was included in the demo project in the link I gave at the beginning of the post, all I did was add my services to the configuration method (as I said before, I'm using a Session object and also WebSocketManager in my app).
_client.GetAsync("/") returns a status of "500 - internal server error" and I have no idea why and how to configure these tests to work.
Any ideas would be appreciated, Thanks.
I have a cached repository
public interface IRepository
{
void LogWebUsage(string html);
IEnumerable<ApiKey> GetApiKeys();
ApiKey GetApiKey(Guid key);
}
public class Repository : IRepository
{
private static readonly ILog Log = LogManager.GetLogger("API.Repository");
public IDbConnectionFactory DbFactory { get; set; }
public void LogWebUsage(string request)
{
Log.Debug(request);
}
public virtual IEnumerable<ApiKey> GetApiKeys()
{
List<ApiKey> result = null;
using (var db = DbFactory.OpenDbConnection())
{
result = db.SelectParam<ApiKey>(q => q.Active);
}
return result;
}
public ApiKey GetApiKey(Guid key)
{
ApiKey result = null;
using (var db = DbFactory.OpenDbConnection())
{
result = (db.SelectParam<ApiKey>(q => q.Id == key)).FirstOrDefault();
}
return result;
}
}
public class CachedRepository : Repository
{
public ICacheClient Cache { get; set; }
public override IEnumerable<ApiKey> GetApiKeys()
{
const string cacheKey = "GetApiKeys";
var result = Cache.Get<IEnumerable<ApiKey>>(cacheKey);
if (result == null)
{
result = base.GetApiKeys();
if (result.Any())
{
Cache.Add(cacheKey, result, TimeSpan.FromMinutes(30));
}
}
return result;
}
}
And I configure it like so.
//Register any dependencies you want injected into your services
container.Register<IDbConnectionFactory>(new OrmLiteConnectionFactory(ConfigUtils.GetConnectionString("DBConnstr"), true, SqlServerOrmLiteDialectProvider.Instance));
container.Register<ICacheClient>(new MemoryCacheClient());
container.Register<IRepository>(new CachedRepository());
container.RegisterAutoWired<CachedRepository>();
So what I was hoping for is that both the IDbConnectionFactory and ICacheClient would be injected at run-time, but they are null. How to you properly account for this type of dependency graph?
Thank you,
Stephen
Updated
After googling for a couple of hours I finally found a solution that works. Constructor injection though the config.
public class CachedRepository : Repository
{
private ICacheClient Cache { get; set; }
public CachedRepository(IDbConnectionFactory dbFactory, ICacheClient cache) : base(dbFactory)
{
Cache = cache;
}
public override IEnumerable<ApiKey> GetApiKeys()
{
const string cacheKey = "GetApiKeys";
var result = Cache.Get<IEnumerable<ApiKey>>(cacheKey);
if (result == null)
{
result = base.GetApiKeys();
if (result.Any())
{
Cache.Add(cacheKey, result, TimeSpan.FromMinutes(30));
}
}
return result;
}
}
Configuration
//Register any dependencies you want injected into your services
container.Register<IDbConnectionFactory>(c => new OrmLiteConnectionFactory(ConfigUtils.GetConnectionString("DBConnstr"), true, SqlServerOrmLiteDialectProvider.Instance));
container.Register<ICacheClient>(c => new MemoryCacheClient());
container.Register<IRepository>(c => new CachedRepository(c.Resolve<IDbConnectionFactory>(), c.Resolve<ICacheClient>()));
It works, but I'd still like to know how to wire up the property injection.
Take care,
Stephen... again
The APIs for AutoWiring in ServiceStack's Funq IOC are here:
Using Generic API:
container.RegisterAutoWired<MyType>();
container.RegisterAutoWiredAs<MyType,IMyType>();
Using Run-time typed API:
container.RegisterAutoWiredType(typeof(MyType));
container.RegisterAutoWiredType(typeof(MyType),typeof(IMyType));
container.RegisterAutoWiredTypes(typeof(MyType),typeof(MyType2),typeof(MyType3));
So basically you can use any of the above APIs to auto-wire your dependencies, e.g:
container.Register<IDbConnectionFactory>(c => new
OrmLiteConnectionFactory(ConfigUtils.GetConnectionString("DBConnstr"), true,
SqlServerDialect.Provider));
container.Register<ICacheClient>(c => new MemoryCacheClient());
container.RegisterAutoWiredAs<CachedRepository,IRepository>(); //auto-wired
I've see n a lot of discussions surrounding HttpSessionState and asp.net MVC.
I'm trying to write tests for an asp.net application and wondering if it's possible to mock the HttpSessionState and if so, how?
I'm currently using Rhino Mocks and Nunit
Gilbert,
Maybe I'm too late for you. I'm using MSpec, but I think the concepts are similar. I needed to mock several components of the HttpContext in the controllers under test.
I started with these following classes to mock up the necessary (for my purposes) components in the HttpContextBase. I overrode only the necessary pieces inside the classes. Your needs will vary as to the mocks you need in the controller. It's fairly easy to add mocks as needed once you understand the pattern.
public class MockHttpContext : HttpContextBase
{
private readonly HttpRequestBase _request = new MockHttpRequest();
private readonly HttpServerUtilityBase _server = new MockHttpServerUtilityBase();
private HttpSessionStateBase _session = new MockHttpSession();
public override HttpRequestBase Request
{
get { return _request; }
}
public override HttpServerUtilityBase Server
{
get { return _server; }
}
public override HttpSessionStateBase Session
{
get { return _session; }
}
}
public class MockHttpRequest : HttpRequestBase
{
private Uri _url = new Uri("http://www.mockrequest.moc/Controller/Action");
public override Uri Url
{
get { return _url; }
}
}
public class MockHttpServerUtilityBase : HttpServerUtilityBase
{
public override string UrlEncode(string s)
{
//return base.UrlEncode(s);
return s; // Not doing anything (this is just a Mock)
}
}
public class MockHttpSession : HttpSessionStateBase
{
// Started with sample http://stackoverflow.com/questions/524457/how-do-you-mock-the-session-object-collection-using-moq
// from http://stackoverflow.com/users/81730/ronnblack
System.Collections.Generic.Dictionary<string, object> _sessionStorage = new System.Collections.Generic.Dictionary<string,object>();
public override object this[string name]
{
get { return _sessionStorage[name]; }
set { _sessionStorage[name] = value; }
}
public override void Add(string name, object value)
{
_sessionStorage[name] = value;
}
}
Here is how I setup the Controller Context to use the mocks (MSpec). This is setup for the actual tests on the contoller (the tests derive from this class)
public abstract class BlahBlahControllerContext
{
protected static BlahBlahController controller;
Establish context = () =>
{
controller = new BlahBlahController();
controller.ControllerContext = new ControllerContext()
{
Controller = controller,
RequestContext = new RequestContext(new MockHttpContext(), new RouteData()),
};
};
}
To further illustrate here is a test (Specification in MSpec world) that uses the mock session:
[Subject("ACCOUNT: Retrieve Password")]
public class retrieve_password_displays_retrieve_password2_page_on_success : BlahBlahControllerContext
{
static ActionResult result;
static RetrievePasswordModel model;
Establish context = () =>
{
model = new RetrievePasswordModel()
{
UserName = "Mike"
};
};
Because of = () =>
{
result = controller.RetrievePassword(model);
};
It should_return_a_RedirectToRouteResult = () =>
{
result.is_a_redirect_to_route_and().action_name().ShouldEqual("RetrievePassword2");
};
It session_should_contain_UN_value = () =>
{
controller.HttpContext.Session["UN"].ShouldEqual("Mike");
};
It session_should_contain_PQ_value = () =>
{
controller.HttpContext.Session["PQ"].ShouldEqual("Question");
};
}
I realize this doesn't use Rhino Mocks. I hope it illustrates the principles and readers can adopt it to their specific tools and methods.
If you need to instantiate exactly HttpSessionState for legacy code tests, you can leverage FormatterServices mechanism to get uninitialized object. To get it working it is needed to set private _container field though, like in internal constructor
Example:
var state = (HttpSessionState) System.Runtime.Serialization
.FormatterServices.GetUninitializedObject(typeof(HttpSessionState));
var containerFld = typeof(HttpSessionState).GetField(
"_container", BindingFlags.Instance | BindingFlags.NonPublic);
var itemCollection = new SessionStateItemCollection();
itemCollection["element"] = 1;
containerFld.SetValue(
state,
new HttpSessionStateContainer(
"1",
itemCollection,
new HttpStaticObjectsCollection(),
900,
true,
HttpCookieMode.UseCookies,
SessionStateMode.InProc,
false
)
);
look at the HttpSessionStateBase and HttpSessionStateWrapper classes in System.Web.Abstractions. HttpSessionStateBase is the abstract class from which HttpSessionState inherits, and HttpSessionStateWrapper is used to wrap a sealed class in an abstract class, which you can then mock in your tests.
A lot of the System.Web classes are sealed (for example, HttpSessionState), so it's a real pain to test your code when you have methods and classes that interact with them. One pattern I like to use to get around this looks like the following:
public void DoSomething(HttpSessionState state)
{
// take this HttpSeassionState and create an abstract HttpSessionStateBase
// instance
DoSomething(new HttpSessionStateWrapper(state));
}
internal void DoSomething(HttpSessionStateBase state)
{
// my actual logic for working with the session state
}
The public method is difficult to test, because HttpSessionState is sealed, and you can't mock it. However, the internal method operates on an HttpSessionStateBase instance, which you can mock. Note that I've marked it as internal because I don't want the outside world to be able to access that method. However, I do want my tests to be able to access that, so I'll modify my AssemblyInfo.cs to include something like this:
[assembly: InternalsVisibleTo("Vendor.Utilities.Tests")]
Finally, my test for this would look something like this:
[Test]
public void Test_DoSomething()
{
HttpSessionStateBase state = MockRepository.PartialMock<HttpSessionStateBase>();
state.Expect(s => ...);
MyClass.DoSomething(state);
state.VerifyAllExpectations();
}
Hope that helps. Good luck!
This is what I made up based on others contribution...
public class MockWebContext
{
public Mock<RequestContext> RoutingRequestContext { get; private set; }
public Mock<HttpContextBase> Http { get; private set; }
public Mock<HttpServerUtilityBase> Server { get; private set; }
public Mock<HttpResponseBase> Response { get; private set; }
public Mock<HttpRequestBase> Request { get; private set; }
public Mock<HttpSessionStateBase> Session { get; private set; }
public Mock<ActionExecutingContext> ActionExecuting { get; private set; }
public HttpCookieCollection Cookies { get; private set; }
private IDictionary items;
public MockWebContext()
{
RoutingRequestContext = new Mock<RequestContext>(MockBehavior.Loose);
ActionExecuting = new Mock<ActionExecutingContext>(MockBehavior.Loose);
Http = new Mock<HttpContextBase>(MockBehavior.Loose);
Server = new Mock<HttpServerUtilityBase>(MockBehavior.Loose);
Response = new Mock<HttpResponseBase>(MockBehavior.Loose);
Request = new Mock<HttpRequestBase>(MockBehavior.Loose);
Session = new Mock<HttpSessionStateBase>(MockBehavior.Loose);
Cookies = new HttpCookieCollection();
items = new Dictionary<string, object>();
RoutingRequestContext.SetupGet(c => c.HttpContext).Returns(Http.Object);
ActionExecuting.SetupGet(c => c.HttpContext).Returns(Http.Object);
Http.SetupGet(c => c.Request).Returns(Request.Object);
Http.SetupGet(c => c.Response).Returns(Response.Object);
Http.SetupGet(c => c.Server).Returns(Server.Object);
Http.SetupGet(c => c.Session).Returns(Session.Object);
Http.SetupGet(c => c.Items).Returns(items);
Request.Setup(c => c.Cookies).Returns(Cookies);
Request.Setup(c => c.RequestContext).Returns(RoutingRequestContext.Object);
Response.Setup(c => c.Cookies).Returns(Cookies);
Session.Setup(c =>
c.Add(It.IsAny<string>(), It.IsAny<object>())
).Callback((string key, object value)=> items.Add(key, value));
Session.Setup(c =>
c.Remove(It.IsAny<string>())
).Callback((string key) => items.Remove(key));
Session.Setup(c =>
c.Clear()
).Callback(() => items.Clear());
Session.Setup(c =>
c[It.IsAny<string>()]
).Returns((string key)=> items[key]);
}
}
Check out the MvcContrib project.
Hi guys, I wrote the following code to return a pdf report. The code is based on a multi-threading sample code. Can you guys provide some feedback about it, I am new to mulit-thread.
Much appriciate !
Jeffery
public delegate void StreamResultDelegate(Stream streamResults);
public class GenerateReport
{
private StreamResultDelegate callback;
public GenerateReport(StreamResultDelegate _callback)
{
callback = _callback;
}
public void ThreadProc()
{
if (callback != null)
{
callback(Testing());
}
}
public Stream Testing()
{
var reportsService = new ReportsService();
var nameValueCollection = new NameValueCollection();
byte[] pdfReportContents = reportsService.GetReport("/Rocket.Reports/RocketReport", nameValueCollection);
var stream = new MemoryStream(pdfReportContents);
return stream;
}
}
//following
[HandleError]
public class HomeController : Controller
{
private Stream streamTesting = null;
public void StreamResultCallBack(Stream s)
{
streamTesting = s;
}
public FileStreamResult GeneratePdfReport()
{
var g = new GenerateReport(_callback: new StreamResultDelegate(StreamResultCallBack));
var t = new Thread(new ThreadStart(g.ThreadProc));
t.Start();
t.Join();
HttpContext.Response.AddHeader("content-disposition", "attachment; filename=Rockets_List_Printout.pdf");
return new FileStreamResult(streamTesting, "application/pdf");
}}
I would suggest to use async controllers -> MSDN and stop using threads in controller methods=)
My suggestion is make a private object to store the result. It's the easiest way
update
public delegate void work_handler(Stream streamResults);
public class Report
{
public object Result = null;
private Thread workThread = new ...;
public void Work(object param)
{
this.Result = ....;
// signal finish. eg. if winapp use someControl.Invoke(signal_Handler);
// for web app use this.Session["isDone"] = true;
}
// for .net 4.0
private object param = null;
public void Work()
{
// for serial invoking
var taskOption = System.Threading.Tasks.TaskCreationOptions.LongRunning;
System.Threading.Tasks.Task task = new System.Threading.Tasks.Task(() => {... }, taskOption);
// for multiple method parallel invoke
System.Threading.Tasks.Parallel.Invoke(() => { this.Result = genReport(param); }, () => {... }, () => {...});
}
}
I've got a thread that sends emails around. I need to generate ActionLinks as part of the content of the email so the user can click on the link and be redirected to the website, exactly to the required page. I tried to instantiate a UrlHelper class and use it's Action method to generate the link but since threads don't run within the context of any request I get exceptions at the time of generating the ActionLink.
How can I do this?
You need to fake HttpContextBase and pass this to an UrlHelper which you can use in a thread without an HttpContext. Here is the rough idea, although you will need to create a class around it etc, this is a quick proof of concept as unit tests don't have an HttpContext either.
[TestFixture]
public class RouteTestClass
{
private UrlHelper helper;
public RouteTestClass()
{
MvcApplication.RegisterRoutes(RouteTable.Routes); //You dont need to do this if its done in global.asax!
var c = new RequestContext(new FakeContext(), new RouteData());
helper = new UrlHelper(c, RouteTable.Routes);
}
[Test]
public void TestGetHomeIndex()
{
var url = helper.Action("Index", "Home");
Assert.AreEqual("/",url);
}
}
public class FakeContext : HttpContextBase
{
public override HttpRequestBase Request { get { return new FakeRequest(); } }
public override HttpResponseBase Response { get { return new FakeResponse(); } }
}
public class FakeRequest : HttpRequestBase
{
public override string ApplicationPath { get { return "/"; } }
public override NameValueCollection ServerVariables { get { return new NameValueCollection(); } }
}
public class FakeResponse : HttpResponseBase
{
public override string ApplyAppPathModifier(string virtualPath)
{
return virtualPath;
}
}
Edit
Looking at this answer, I tidied the code up a little as I don't need to create fakes for HttpRequestBase and HttpResponseBase myself.
[TestFixture]
public class RouteTestClass
{
private UrlHelper helper;
public RouteTestClass()
{
MvcApplication.RegisterRoutes(RouteTable.Routes);
var req = new HttpRequest("/", "http://www.yoururl.com", "");
var resp = new HttpResponse(new StringWriter());
var httpContext = new HttpContext(req, resp);
var c = new RequestContext(new HttpContextWrapper(httpContext), new RouteData());
helper = new UrlHelper(c, RouteTable.Routes);
}
[Test]
public void TestGetHomeIndex()
{
var url = helper.Action("Index", "Home");
Assert.AreEqual("/",url);
}
}
You can give the thread access to an existing UrlHelper by passing it to the thread starter. If your thread is started from a controller, just pass the UrlHelper in the controller's Url property:
new Thread(
urlHelper =>
{
var url =
((UrlHelper)urlHelper)
.Action("Index", "Home", new { Id = 5 });
// use url here
}
).Start(Url);