Unit Testing Web Service Session variable with NUnit and Moq - c#

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!

Related

System.NullReferenceException when unit testing

I am new to unit testing,
I am working on a minimal API project. and I am testing an endpoint with xunit, moq
endpoint class - ParticipantsGetAll.cs
public class ParticipantsGetAll : IEndpoint<IResult, GetAllParticipantsRequest>
{
const string uri = "api/participants-all";
private ParticipantService? participantService;
public void AddRoute(IEndpointRouteBuilder app)
{
app.MapPost(uri, async ( ParticipantService participantService, [FromBody] GetAllParticipantsRequest query) =>
{
this.participantService = participantService;
return await HandleAsync(query);
})
.Produces<List<ParticipantSummaryModel>>()
.WithTags("Participants")
.WithName("GetAllParticipants");
}
public async Task<IResult> HandleAsync( GetAllParticipantsRequest query)
{
var participants = await participantService!.ListAllAsync(query);
return Results.Ok(participants);
}
I tried to write a unit test test above endpoint class.
CreateParticipantApiTest.cs
[Fact]
public async void ListAllAsyncShouldReturn200Status()
{
var query = new GetAllParticipantsRequest()
{
Name= "",
KeyCordinator="",
RelatedConnection="",
Statuses = null
};
var participant = ParticipantMockData.NewParticipantModel(); // a static class
var sut = new ParticipantsGetAll();
var result = (OkObjectResult)await sut.HandleAsync(query);
result.StatusCode.Should().Be(200);
}
I got below error
Message: 
System.NullReferenceException : Object reference not set to an instance of an object.
Stack Trace: 
ParticipantsGetAll.HandleAsync(GetAllParticipantsRequest query) line 36
CreateParticipantApiTest.ListAllAsyncShouldReturn200Status() line 67
I have no idea why the object tis null.
Please anyone help me to find the problem.
I am facing this issue for a long time
Thanks
I have no idea why the object tis null.
That is pretty clear, because in your unit test you invoke HandleAsync directly, so the setup which you have moved into MapPost does not happen (compiler was trying to help but was shut down with null-forgiving operator in participantService!.ListAllAsync(query)). Also I'm pretty sure this way of using handlers can lead to some concurrency problems (if ParticipantService is a scoped service). Move ParticipantService participantService to HandleAsync. Something along this lines (not tested):
public class ParticipantsGetAll : IEndpoint<IResult, GetAllParticipantsRequest>
{
const string uri = "api/participants-all";
public void AddRoute(IEndpointRouteBuilder app)
{
app.MapPost(uri, HandleAsync)
.Produces<List<ParticipantSummaryModel>>()
.WithTags("Participants")
.WithName("GetAllParticipants");
}
public async Task<IResult> HandleAsync(ParticipantService participantService, [FromBody] GetAllParticipantsRequest query)
{
var participants = await participantService.ListAllAsync(query);
return Results.Ok(participants);
}
}
And modify the test accrodingly.

Unit testing an AuthorizeAttribute on an ASP.NET Core MVC API controller

I have a ASP.NET Core MVC API with controllers that need to be unit tested.
Controller:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
namespace TransitApi.Api.Controllers
{
[Route("api/foo")]
public class FooController : Controller
{
private IFooRepository FooRepository { get; }
public FooController(IFooRepository fooRepository)
{
FooRepository = fooRepository;
}
[HttpGet]
[Authorize("scopes:getfoos")]
public async Task<IActionResult> GetAsync()
{
var foos = await FooRepository.GetAsync();
return Json(foos);
}
}
}
It is essential that I am able to unit test the effectiveness of the AuthorizeAttribute. We have had issues in our code base with missing attributes and incorrect scopes. This answer is exactly what I am looking for, but not having a ActionInvoker method in Microsoft.AspNetCore.Mvc.Controller means I am not able to do it this way.
Unit Test:
[Fact]
public void GetAsync_InvalidScope_ReturnsUnauthorizedResult()
{
// Arrange
var fooRepository = new StubFooRepository();
var controller = new FooController(fooRepository)
{
ControllerContext = new ControllerContext
{
HttpContext = new FakeHttpContext()
// User unfortunately not available in HttpContext
//,User = new User() { Scopes = "none" }
}
};
// Act
var result = controller.GetAsync().Result;
// Assert
Assert.IsType<UnauthorizedResult>(result);
}
How can I unit test that users without the correct scopes are denied access to my controller method?
Currently I have settled for testing merely the presence of an AuthorizeAttribute as follows, but this is really not good enough:
[Fact]
public void GetAsync_Analysis_HasAuthorizeAttribute()
{
// Arrange
var fooRepository = new StubFooRepository();
var controller = new FooController(fooRepository)
{
ControllerContext = new ControllerContext
{
HttpContext = new FakeHttpContext()
}
};
// Act
var type = controller.GetType();
var methodInfo = type.GetMethod("GetAsync", new Type[] { });
var attributes = methodInfo.GetCustomAttributes(typeof(AuthorizeAttribute), true);
// Assert
Assert.True(attributes.Any());
}
This would need integration testing with an in-memory test server because the attribute is evaluated by the framework as it processes the request pipeline.
Integration testing in ASP.NET Core
Integration testing ensures that an application's components function correctly when assembled together. ASP.NET Core supports integration testing using unit test frameworks and a built-in test web host that can be used to handle requests without network overhead.
[Fact]
public async Task GetAsync_InvalidScope_ReturnsUnauthorizedResult() {
// Arrange
var server = new TestServer(new WebHostBuilder().UseStartup<Startup>());
var client = server.CreateClient();
var url = "api/foo";
var expected = HttpStatusCode.Unauthorized;
// Act
var response = await client.GetAsync(url);
// Assert
Assert.AreEqual(expected, response.StatusCode);
}
You can also create a start up specifically for the test that will replace any dependencies for DI with stubs/mocks if you do not want the test hitting actual production implementations.
What you could do, is to configure your testserver to add an anonymous filter middleware:
private HttpClient CreatControllerClient()
{
return _factory.WithWebHostBuilder(builder
=> builder.ConfigureTestServices(services =>
{
// allow anonymous access to bypass authorization
services.AddMvc(opt => opt.Filters.Add(new AllowAnonymousFilter()));
})).CreateClient();
}
First remeove IAuthorizationHandler
var authorizationDescriptor = services.FirstOrDefault(d => d.ServiceType == typeof(IAuthorizationHandler));
if (authorizationDescriptor != null)
services.Remove(authorizationDescriptor);
Then add
services.AddScoped<IAuthorizationHandler, TestAllowAnonymous>();
public class TestAllowAnonymous : IAuthorizationHandler
{
public Task HandleAsync(AuthorizationHandlerContext context)
{
foreach (IAuthorizationRequirement requirement in context.PendingRequirements.ToList())
context.Succeed(requirement); //Simply pass all requirementsreturn Task.CompletedTask;
return Task.CompletedTask;
}
}

Assert an Exception using XUnit

I am a newbie to XUnit and Moq. I have a method which takes string as an argument.How to handle an exception using XUnit.
[Fact]
public void ProfileRepository_GetSettingsForUserIDWithInvalidArguments_ThrowsArgumentException() {
//arrange
ProfileRepository profiles = new ProfileRepository();
//act
var result = profiles.GetSettingsForUserID("");
//assert
//The below statement is not working as expected.
Assert.Throws<ArgumentException>(() => profiles.GetSettingsForUserID(""));
}
Method under test
public IEnumerable<Setting> GetSettingsForUserID(string userid)
{
if (string.IsNullOrWhiteSpace(userid)) throw new ArgumentException("User Id Cannot be null");
var s = profiles.Where(e => e.UserID == userid).SelectMany(e => e.Settings);
return s;
}
The Assert.Throws expression will catch the exception and assert the type. You are however calling the method under test outside of the assert expression and thus failing the test case.
[Fact]
public void ProfileRepository_GetSettingsForUserIDWithInvalidArguments_ThrowsArgumentException()
{
//arrange
ProfileRepository profiles = new ProfileRepository();
// act & assert
Assert.Throws<ArgumentException>(() => profiles.GetSettingsForUserID(""));
}
If bent on following AAA you can extract the action into its own variable.
[Fact]
public void ProfileRepository_GetSettingsForUserIDWithInvalidArguments_ThrowsArgumentException()
{
//arrange
ProfileRepository profiles = new ProfileRepository();
//act
Action act = () => profiles.GetSettingsForUserID("");
//assert
ArgumentException exception = Assert.Throws<ArgumentException>(act);
//The thrown exception can be used for even more detailed assertions.
Assert.Equal("expected error message here", exception.Message);
}
Note how the exception can also be used for more detailed assertions
If testing asynchronously, Assert.ThrowsAsync follows similarly to the previously given example, except that the assertion should be awaited,
public async Task Some_Async_Test() {
//...
//Act
Func<Task> act = () => subject.SomeMethodAsync();
//Assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(act);
//...
}
If you do want to be rigid about AAA then you can use Record.Exception from xUnit to capture the Exception in your Act stage.
You can then make assertions based on the captured exception in the Assert stage.
An example of this can be seen in xUnits tests.
[Fact]
public void Exception()
{
Action testCode = () => { throw new InvalidOperationException(); };
var ex = Record.Exception(testCode);
Assert.NotNull(ex);
Assert.IsType<InvalidOperationException>(ex);
}
It's up to you what path you want to follow, and both paths are fully supported by what xUnit provides.
You could consider something like this if you want to stick to AAA:
// Act
Task act() => handler.Handle(request);
// Assert
await Assert.ThrowsAsync<MyExpectedException>(act);
I think there are two way to handle this scenario which I personally like. Suppose I have below method which I want to test
public class SampleCode
{
public void GetSettingsForUserID(string userid)
{
if (string.IsNullOrWhiteSpace(userid)) throw new ArgumentException("User Id
Cannot be null");
// Some code
}
}
I can test this with below testcase,Make sure you add FluentAssertions nuget in your test project.
public class SampleTest
{
private SampleCode _sut;
public SampleTest()
{
_sut = new SampleCode();
}
[Theory]
[InlineData(null)]
[InlineData(" ")]
public void TestIfValueIsNullorwhiteSpace(string userId)
{
//Act
Action act= ()=> _sut.GetSettingsForUserID(userId);
// Assert
act.Should().ThrowExactly<ArgumentException>().WithMessage("User Id Cannot be null");
}
}
but I found one problem here, whitespace and Null are two different things.
c# provides ArgumentException for whitespace and ArgumentNullException for null reference.
So You can refactor your code something like this
public void GetSettingsForUserID(string userid)
{
Guard.Against.NullOrWhiteSpace(userid, nameof(userid));
}
Here you need Ardalis.GuardClauses nuget in your code project And testcase will be something like this
[Fact]
public void TestIfValueIsNull()
{
//Act
Action act = () => _sut.GetSettingsForUserID(null);
//Assert
act.Should().ThrowExactly<ArgumentNullException>().WithMessage("*userId*");
}
[Fact]
public void TestIfValueIsWhiteSpace()
{
//Act
Action act= ()=> _sut.GetSettingsForUserID(" ");
//Assert
act.Should().ThrowExactly<ArgumentException>().WithMessage("*userId*");
}
Instead of following complicated protocols I found it most convenient to use a try catch block :
try
{
var output = Settings.GetResultFromIActionResult<int>(controller.CreateAllFromExternalAPI());
Assert.True(output > 0);
}
catch(InvalidOperationException e)
{
Assert.True("Country table can only be filled from ExternalAPI if table is blank"==e.Message);
}

C# TDD, Integration Test Repository, Verify Add Test

I'm relatively new to TDD and I have a project with repository pattern with EF in the repository class.
Now I want to test these repositories and I have already mocked the DbContext successfully. (Using Moq and nunit)
Now I want to validate adding a object to my mocked database. The test says, test passed, but I'm quite sure, my test is not correct because I queried the database before the Add method, returns 5 objects, then I call the Add method and query the database again, still only 5 objects, but I would expect now 6 objects. Am I missing here something?
Model Address.cs
public class Address
{
public int Id { get; set; }
public string Person {get; set; }
//some more properties
}
Repository.cs
public async Task<AdresseDto> AddAdresseAsync(AdresseDto adresseDto)
{
try
{
ctx.Adresse.Add(adresse);
await ctx.SaveChangesAsync();
adresseDto.Id = adresse.Id;
}
catch (Exception e)
{
Console.WriteLine("ohoh");
Console.WriteLine(e.ToString());
Console.WriteLine("ohoh");
return null;
}
return adresseDto;
}
UnitTest.cs
public async Task Test_AddTestAddress_ReturnsAddressWithId()
{
var dummyDataInDb = AddressHelper.GetAdressen(5);
var dummyData4Db = AddressHelper.GetAdressen(1).FirstOrDefault();
//AddressHelper returns just some dummy objects
InitializeMock<Adresse>.With(dummyDataInDb.AsQueryable(), out AdresseMock);
XaDbMock = new Mock<xaModel>();
XaDbMock
.Setup(x => x.Adresse)
.Returns(AdresseMock.Object);
_repository = NewAddressRepository(XaDbMock.Object);
//When
var addressesInDb = await _repository.GetAllAdressenAsync();
var result = await _repository.AddAdresseAsync(dummyData4Db);
var addressesInDbAfter = await _repository.GetAllAdressenAsync();
//Then
Assert.IsNotNull(addressesInDb);
Assert.AreEqual(5, addressesInDb.Count);
Assert.IsNotNull(result);
Assert.AreEqual(0, result.Id);
Assert.IsNotNull(addressesInDbAfter);
Assert.AreEqual(6, addressesInDbAfter.Count);
}
InitializeMock.cs
public class InitializeMock<T> where T : class
{
public static void With(IQueryable<T> dummyData, out Mock<DbSet<T>> mock)
{
mock = new Mock<DbSet<T>>();
//Required for async tasks
mock.As<IDbAsyncEnumerable>()
.Setup(x => x.GetAsyncEnumerator())
.Returns(new TestDbAsyncEnumerator<T>(dummyData.GetEnumerator()));
//Required for async tasks
mock.As<IQueryable<T>>()
.Setup(x => x.Provider)
.Returns(new TestDbAsyncQueryProvider<T>(dummyData.Provider));
mock.As<IQueryable<T>>()
.Setup(x => x.Expression)
.Returns(dummyData.Expression);
mock.As<IQueryable<T>>()
.Setup(x => x.ElementType)
.Returns(dummyData.ElementType);
mock.As<IQueryable<T>>()
.Setup(x => x.GetEnumerator())
.Returns(dummyData.GetEnumerator);
}
}
If something is missing, please let me know. I'll provide any neccessary informations you need.
Thanks in advance

How to test session.clear() in Mocking unit test

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?

Categories

Resources