I am presently working on unit testing using Microsoft Fakes and testing a controller which uses some session variables.
Since no session is being initiated during unit test creation whenever I'm running unit test I'm stuck with a NullReferenceException.I have seen many question and answers doing it using Moqs but I want it in Microsoft Fakes.
I know I need to use shims to fake session variable ,since I'm not having a clear idea about how exactly session is being created I'm stuck there.
Please explain me how exactly a session is created so that I can fake it,also if possible please let me know how to write it in microsoft fakes
Well, you can this as example :
public class SomeClass
{
public bool SomeMethod()
{
var session = HttpContext.Current.Session;
if (session["someSessionData"].ToString() == "OK")
return true;
return false;
}
}
[TestMethod]
public void SomeTestMethod()
{
using (ShimsContext.Create())
{
var instanceToTest = new SomeClass();
var session = new System.Web.SessionState.Fakes.ShimHttpSessionState();
session.ItemGetString = (key) => { if (key == "someSessionData") return "OK"; return null; };
var context = new System.Web.Fakes.ShimHttpContext();
System.Web.Fakes.ShimHttpContext.CurrentGet = () => { return context; };
System.Web.Fakes.ShimHttpContext.AllInstances.SessionGet =
(o) =>
{
return session;
};
var result = instanceToTest.SomeMethod();
Assert.IsTrue(result);
}
}
Please see http://blog.christopheargento.net/2013/02/02/testing-untestable-code-thanks-to-ms-fakes/ for more details.
Have a nice day.
Related
I have met a following problem: I have to write a test for a method which is a part of a service, but does not make use of other parts of said service, except for a two methods (which is called Remove and is presented below).
Method I need to write a test for takes in a cookie name. Then, based on that cookie name it gets from dictionary a consent category said cookie belongs to. After that there is IF statement which uses the HasCookieConsent method and determines if cookie should be removed, or not. If yes, it is removed by Remove method.
public void UpdateCookiesAccordingToConsent(string cookie)
{
var cookiesConsentType = _httpOnlyCookies.FirstOrDefault(x => x.Key ==
cookie).Value;
if (!HasCookieConsent(cookiesConsentType) && _httpOnlyCookies.ContainsKey(cookie))
{
Remove(cookie);
}
}
Cookie categories are taken from dictionary:
private readonly Dictionary<string, CookiesConsentType> _httpOnlyCookies = new Dictionary<string, CookiesConsentType>()
{
{ CookieNames.VisitorCookieName, CookiesConsentType.Statistic },
{ CookieNames.GoogleAnalyticsTrackingCookieName, CookiesConsentType.Statistic },
{ CookieNames.TaxonomyVisitorCookieName, CookiesConsentType.Statistic },
{ CookieNames.MarketoMunchkinTrackingCookieName, CookiesConsentType.Marketing },
};
Remove method:
public void Remove(string cookie)
{
if (_httpContextAccessor.HttpContext == null)
{
return;
}
var options = new CookieOptions
{
HttpOnly = true,
Secure = _httpContextAccessor.HttpContext.Request.IsHttps,
Expires = DateTime.Now.AddDays(-1),
};
_httpContextAccessor.HttpContext.Response.Cookies.Append(cookie, string.Empty, options);
}
HasCookieConsent method:
private bool HasCookieConsent(CookiesConsentType consentType)
{
try
{
var hasConsentCookie = _httpContextAccessor?.HttpContext?.Request?.Cookies?.ContainsKey("CookieConsent") ?? false;
if (!hasConsentCookie)
{
return false;
}
var cookie = _httpContextAccessor.HttpContext.Request.Cookies["CookieConsent"] ?? string.Empty;
if (string.IsNullOrWhiteSpace(cookie))
{
return false;
}
var cookieConsent = JsonConvert.DeserializeObject<CookieConsent>(cookie) ?? new CookieConsent();
return consentType switch
{
CookiesConsentType.Preferences => cookieConsent.Preferences,
CookiesConsentType.Marketing => cookieConsent.Marketing,
CookiesConsentType.Statistic => cookieConsent.Statistics,
CookiesConsentType.Necessary => cookieConsent.Necessary,
_ => false,
};
}
catch (Exception ex)
{
_logger.LogError("Could not deserialize cookie: {Exception}", ex);
return false;
}
}
Any tips on how to do it? I am using xUnit.
In order to test this class as-is you'll need to create an HttpContext with a request that contains the cookies you want. Then you'll need an IHttpContextAccessor which returns that HttpContext.
It looks like you're injecting the IHttpContextAccessor into your class, like this:
public class YourClass
{
private IHttpContextAccessor _httpContextAccessor;
public YourClass(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
// all your other methods
}
In your test you'll
Create an instance of YourClass and inject the IHttpContextAccessor you created.
Call the methods of YourClass
Assert that the response cookies are what you expect them to be.
In this case you don't need to create any mocks. You can use existing implementations.
Here's what a test would look like. It's vague because I'm not trying to follow the specifics of what your code does and I'm not sure what the expected result is supposed to be.
public void UpdateCookiesAccordingToConsent_does_whatever()
{
// Create an HttpContext and add some request cookies to it
var httpContext = new DefaultHttpContext();
httpContext.Request.Cookies = new RequestCookieCollection(new Dictionary<string, string>()
{
{ "cookieName", "cookieValue" },
{ "cookieName2", "cookieValue2" }
});
// Create an HttpContextAccessor that returns your HttpContext
var contextAccessor = new HttpContextAccessor
{
HttpContext = httpContext
};
// Create an instance of your class. When it gets the current HttpContext
// it will get the one you created.
var testSubject = new YourClass(contextAccessor);
// Call some method on your class.
testSubject.UpdateCookiesAccordingToConsent("whatever");
// Presumably executing that method has done something to the cookies.
// Assert that the cookies in httpContext contain what you expect.
}
Here's another approach. Going this route depends upon your coding style.
If the logic in one of the methods becomes very complex or you find yourself having to reuse it, you could move it into a separate method, even an extension in another class.
Here's an example:
public static bool HasCookieConsent(this IRequestCookieCollection requestCookies)
{
try
{
var hasConsentCookie = requestCookies?.ContainsKey("CookieConsent") ?? false;
if (!hasConsentCookie)
{
return false;
}
var cookie = requestCookies["CookieConsent"] ?? string.Empty;
if (string.IsNullOrWhiteSpace(cookie))
{
return false;
}
var cookieConsent = JsonConvert.DeserializeObject<CookieConsent>(cookie) ?? new CookieConsent();
return consentType switch
{
CookiesConsentType.Preferences => cookieConsent.Preferences,
CookiesConsentType.Marketing => cookieConsent.Marketing,
CookiesConsentType.Statistic => cookieConsent.Statistics,
CookiesConsentType.Necessary => cookieConsent.Necessary,
_ => false,
};
}
catch (Exception ex)
{
_logger.LogError("Could not deserialize cookie: {Exception}", ex);
return false;
}
}
The method is modified not to depend on IHttpContextAccessor or HttpContext. It doesn't care about those things. It only cares about the cookies, so those are passed as a parameter.
It's easy to test this method by creating a RequestCookiesCollection (like in the previous example), passing it to this method, and asserting that the method returns true or false as expected.
That's a coding preference. Some people prefer to have more private methods and test them all through the public ones. In other cases we might extract methods into their own classes for testing. In each case I'd go with whatever makes your tests easier to write. I'm not selling one or the other, just presenting options.
try something like this.
[Test]
public void UpdateCookiesAccordingToConsent_CallsRemove_WhenHasCookieConsentIsFalseAndHttpOnlyCookiesContainsCookie()
{
// Arrange
var cookie = "testCookie";
var cookiesConsentType = CookiesConsentType.Statistic;
var httpOnlyCookies = new Dictionary<string, CookiesConsentType>()
{
{ cookie, cookiesConsentType }
};
var service = new CookieService(httpOnlyCookies);
service.HasCookieConsent(false);
// Act
service.UpdateCookiesAccordingToConsent(cookie);
// Assert
service.Remove(cookie);
}
I have bunch of tests class such as CustomerTest, ProductTest, VendorTest and so on. I'm using in-memory Database and I would like to seed all data that I need to ONLY ONCE before running all these Tests above but have no idea how to do it.
I have CustomWebApplicationFactory class
public class CustomWebApplicationFactory<TProgram> : WebApplicationFactory<Program>
{
public ApplicationContext context { get; set; }
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureTestServices(services => {
var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<ApplicationContext>));
if (descriptor != null)
services.Remove(descriptor);
services.AddDbContext<ApplicationContext>(options => options
.UseInMemoryDatabase("testDB")
);
var serviceProvider = services.BuildServiceProvider();
using (var scope = serviceProvider.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var db = scopedServices.GetRequiredService<ApplicationContext>();
db.Database.EnsureDeleted();
db.Database.EnsureCreated();
SeedData(db); //seeding all data to all table
}
});
}
}
My Tests class
[TestClass]
public class VendorTest : BaseTest
{
[TestMethod]
public async Task AddVendor()
{
var content = JsonSerializer.Serialize(
new
{
name = "VENDORTEST"
}
);
var response = await _httpClient.PostAsync(
"vendors",
new StringContent(content, Encoding.UTF8, "application/json")
);
Assert.IsTrue(response.IsSuccessStatusCode);
}
[TestMethod]
public async Task GetVendor()
{
var response = await _httpClient.GetAsync("vendors");
string result = await response.Content.ReadAsStringAsync();
Assert.IsNotNull(response);
}
}
Base Test
public class BaseTest
{
protected static CustomWebApplicationFactory<Program> _webAppFactory = new CustomWebApplicationFactory<Program>();
protected static HttpClient _httpClient = _webAppFactory.CreateDefaultClient();
[AssemblyInitialize]
public void Initialize()
{
//not sure if BaseClass is the way to do it
//_httpClient = _webAppFactory.CreateDefaultClient();
}
}
Everytime a TestMethod is running, it will reseed all of the data due to CustomWebApplicationFactory. Any idea how to do it only once?
First of all: I dislike your idea so much, that I hesitated to show you a possible approach. I'll explain that after the code.
If you need something, that runs only once, static things come to mind. So, you could use a static/singleton factory to create your in-memory database like this:
public class TestDbFactory
{
private static TestDbFactory instance;
private readonly ApplicationContext applicationContext;
private TestDbFactory()
{
var databaseName = Guid.NewGuid().ToString();
var inMemoryContextOptions = new DbContextOptionsBuilder<ApplicationContext>()
.UseInMemoryDatabase(databaseName)
.Options;
this.applicationContext = new ApplicationContext(inMemoryContextOptions);
this.InitDatabase();
}
public static TestDbFactory Instance => CreateOrReuseInstance();
public ApplicationContext ApplicationContext => this.applicationContext;
private static TestDbFactory CreateOrReuseInstance()
{
if (instance != null) return instance;
var semaphore = new SemaphoreSlim(1, 1);
instance = instance ?? new TestDbFactory();
semaphore.Release(1);
return instance;
}
private void InitDatabase()
{
// ensure deleted & created & seeded for this.applicationContext
}
}
Now you can either use TestDbFactory.Instance.ApplicationContext to pass the database to the systems under test or you can use the factory pattern of the dependency injection setup to provide the context:
services.AddScoped(_ => TestDbFactory.Instance.ApplicationContext);
What I don't like about your idea:
Even for a single test you have to seed the whole database.
You can't do write test, because then tests don't have a predictable setup (it might be or not, that the db contents have changed before a test runs).
To me a test also serves as documentation. This is not possible, if the database content relevant for a specific test can't be determined from the test.
Adding new tests and updating the seeding will become a nightmare as you (let alone a whole team) have to verify the new data vs. all existing tests.
You won't save much typing as you still have to data for every test.
So, to me a better approach would be to use an in-memory database in every test class (a test class contains all tests for a single method). It would be ok to provide a basic seeding common to all tests in the test class, but specific data setup for a test should go with the test.
I hope that this post helps in one way or the other.
How can i test a method which clear the session and logoff user.
my controller method looks like
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
SessionAdapter.Clear();
SessionAdapter.Abandon();
AuthenticationManager.SignOut
(DefaultAuthenticationTypes.ApplicationCookie);
return RedirectToAction("Index", "Home");
}
Here session adapter is my static class
public static class SessionAdapter
{
private static string sessionKey = "sessionInfoKey";
public static SessionInfo Instance
{
get
{
return HttpContext.Current.Session[sessionKey] == null ? null : (SessionInfo)HttpContext.Current.Session[sessionKey];
}
set
{
HttpContext.Current.Session[sessionKey] = value;
}
}
public static bool DoesSessionExists { get { return HttpContext.Current.Session[sessionKey] == null ? false : true; } }
public static void Clear()
{
HttpContext.Current.Session.Clear();
}
}
Please help me
In a unit test you should mocking out session since it's an external dependency. Your unit tests should be testing your code, not the .net framework.
So a valid test might be to verify that a call to Session.Clear happened, not actually testing Session.Clear clears out a session.
This can be done by setting up a fake session. Here's a extension method I use to setup my controller context for unit testing
public static void SetControllerContext(this Controller controller)
{
var fakeContext = A.Fake();
var fakeRequest = A.Fake();
var fakeResponse = A.Fake();
var fakeSessionState = A.Fake();
A.CallTo(() => fakeRequest.HttpMethod).Returns(HttpVerbs.Post.ToString());
A.CallTo(() => fakeContext.Response).Returns(fakeResponse);
A.CallTo(() => fakeContext.Request).Returns(fakeRequest);
A.CallTo(() => fakeContext.Session).Returns(fakeSessionState);
var fakeRequestContext = new RequestContext(fakeContext, new RouteData());
controller.ControllerContext = new ControllerContext(fakeRequestContext, controller);
}
This is using FakeItEasy, but the same thing can be done with Moq.
From MS "ASP.NET session state enables you to store and retrieve values for a user as the user navigates ASP.NET pages in a Web application."
What's the value in wrapping that in a static class?
I want to test WebMethod of some Web Service (asmx). Suppose I have the following code:
public IUsersRepository UsersRepository
{
get { return Session["repository"] as IUsersRepository; }
set { Session["repository"] = value; }
}
[WebMethod(EnableSession = true)]
public int AddUser(string userName, int something)
{
var usersRepository = Session["repository"] as IUsersRepository;
return usersRepository.AddUser(userName, something);
}
and the corresponding unit test (just to test that the repository is called at all):
[Test]
public void add_user_adds_user()
{
// Arrange
var repository = new Mock<IUsersRepository>();
var service = new ProteinTrackingService { UsersRepository = repository.Object };
// Act
var userName = "Tester";
var something = 42;
service.AddUser(userName: userName, something: something);
// Assert
repository.Verify(r => r.AddUser(
It.Is<string>(n => n.Equals(userName)),
It.Is<int>(p => p.Equals(something))));
}
When I run this test, I receive the following error message:
System.InvalidOperationException : HttpContext is not available.
This class can only be used in the context of an ASP.NET request.
What shall I do to make this test working?
Have you had a look at this one? Setting HttpContext.Current.Session in a unit test Apparently should can do that trick to simulate your session.
On regards to your assert, you can directly do:
// Assert
repository.Verify(r => r.AddUser(userName, something));
And that will assert you are calling that method with these parameters.
Hope this helps!
I'd like to make my ServiceStack service testable.
Presently I have:
[RequireFormsAuthentication]
public object Delete(DeleteRequest request)
{
var originalRequest = (HttpRequest)Request.OriginalRequest;
var identity = originalRequest.RequestContext.HttpContext.User.Identity;
return othercode(identity);
}
Where RequireFormsAuthentication is
public class RequireFormsAuthenticationAttribute : RequestFilterAttribute
{
public override void Execute(IHttpRequest req, IHttpResponse res, object requestDto)
{
var originalRequest = (HttpRequest)req.OriginalRequest;
var identity = originalRequest.RequestContext.HttpContext.User.Identity;
if (!identity.IsAuthenticated)
{
res.StatusCode = (int)HttpStatusCode.Forbidden;
res.EndServiceStackRequest(skipHeaders: true);
}
}
}
I've mocked out all the dependencies used by 'othercode()' and all that's left is the stuff that's in the base class Service. Is there a pattern/strategy/approach/something I'm missing that makes this trivial?
Here's how to test with Moq. This service looks for a "key" and "value" in the query string and another parameter in the request DTO. The service returns a string response based on the value given.
[Test]
public void MyTest()
{
var mockRequestContext = new Mock<IRequestContext>();
var mockedHttpRequest = new Mock<IHttpRequest>();
NameValueCollection querystring = new NameValueCollection();
querystring.Add("myKey", "myValue");
mockedHttpRequest.SetupGet(r => r.QueryString).Returns(querystring);
mockRequestContext.Setup(x => x.Get<IHttpRequest>()).Returns(mockedHttpRequest.Object);
AboutService service = new AboutService
{
RequestContext = mockRequestContext.Object,
};
AboutResponse response = (AboutResponse)service.Any(new About
{
Company = "myOtherValue",
});
Assert.AreEqual(0, response.QueryResult);
Assert.AreEqual("validResponse", response.Version);
}
I apologize for not using moq...already had some of this done using RhinoMocks. I think the concept should transfer to moq. This might be a good resource as well as this this.
Anyway, I think the test code below should get you started. Your seam into mocking Request.OriginalRequest is replaceing the Service.RequestContext with a mock object. Then you just have to mock everything beyond that. It's going to be a lot of 'mocking' and if you repeat to yourself 'Are you mocking me' every time you mock a class it's almost enjoyable.
[Test]
public void testsomethign()
{
var mockedRequestContext = MockRepository.GenerateMock<IRequestContext>();
var mockedHttpRequest = MockRepository.GenerateMock<IHttpRequest>();
var mockedOriginalRequest = MockRepository.GenerateMock<HttpRequestBase>();
var mockedOriginalRequestContext = MockRepository.GenerateMock<RequestContext>();
mockedOriginalRequest.Stub(x => x.RequestContext).Return(mockedOriginalRequestContext);
mockedHttpRequest.Stub(x => x.OriginalRequest).Return(mockedOriginalRequest);
mockedRequestContext.Stub(x => x.Get<IHttpRequest>()).Return(mockedHttpRequest);
var service = new ServiceTests()
{
RequestContext = mockedRequestContext
};
service.Delete(new DeleteRequest());
}
Be sure to check out the namespace: ServiceStack.ServiceInterface.Testing.
In there you can find a MockRequestContext that you can use as follows:
var mockContext = new ServiceStack.ServiceInterface.Testing.MockRequestContext();
//do stuff to set it up if desired...
AboutService service = new AboutService
{
RequestContext = mockContext
};