Mocking out the Data Accessor - c#

I am trying to learn about using Moq with NUnit and IoC.
(I have my full project in BitBucket, but not sure how to share it...)
https://bitbucket.org/Cralis/skeleton/overview
I have a Logic method (Login) I am trying to test. It take a request object (Which has a username, password and IP Address). If the username and/or password are empty, the logic returns a failed status, and doesn't go to the data access layer.
So I am creating a unit test to test this.
(This my my first attempt with mocking...)
public void NotNull_Returns_True()
{
// Arrange
var request = new LoginRequest { IPAddress = "1.1.1.1", Username = "dummy", Password = "dummy" };
var response = new LoginResponse { Request = request, Success = true, Message = "", UserID = 1 };
// Setup the moc data accessor, as we don't want to gop to the concrete one.
var MockedDataAccess = new Mock<IDataAccess>();
// Set it's return value
MockedDataAccess.Setup(x => x.Login(request)).Returns(response);
// Instantiate the Logic class we're testing, using a Moc data accessor.
var logic = new BusinessLogic(MockedDataAccess.Object);
// Act
var result = logic.Login(new LoginRequest { Password = "dummy", Username = "dummy", IPAddress = "1.1.1.1" });
// Assert
Assert.AreEqual(true, result.Success);
}
This fails on the assert, as 'result' is NULL.
I'm probably doing a lot wrong. For example, I'm not sure why I need to setup the request and response objects at the top,but because all the examples I find are 'string' and 'int' inputs, it seems I can't use It.IsAny...
Could someone assist me understanding here? What am I doing wrong to get NULL as a result in the assert? I step through and the code executes as expected. But the result is null, because I never called the data accessor (It used the mock).
Edit:
Ah,
// Set it's return value
MockedDataAccess.Setup(x => x.Login(It.IsAny<LoginRequest>())).Returns(response);
That resolved the issue. I'm not sure why, so if you can help me understand and refactor this so that it's as an experienced Moq/UnitTester would expect it to look, that would be very useful.

Even though your request object has the same property values that you're passing to var result = logic.Login(new LoginRequest { Password = "dummy", Username = "dummy", IPAddress = "1.1.1.1" });, they are different objects, and so the value that you're trying to return with MockedDataAccess.Setup(x => x.Login(request)).Returns(response); isn't getting returned.
Change
var result = logic.Login(new LoginRequest { Password = "dummy", Username = "dummy", IPAddress = "1.1.1.1" });
to
var result = logic.Login(request);
The reason it worked with MockedDataAccess.Setup(x => x.Login(It.IsAny<LoginRequest>())).Returns(response); is because now you're saying "when MockedDataAccess.Login is called with any value for its parameter, return response"
Regarding the second part of your question, the reason that you need to set up the request and response objects is that by default any method that you call on a mock object will return null. Your BusinessLogic.Login method, listed below, will return the value of dataAccess.Login(). Since dataAccess is a mock, the dataAccess.Login() method will return null unless you tell it otherwise.
public LoginResponse Login(LoginRequest request)
{
// Basic validation
if (request == null)
return new LoginResponse
{
Success = false,
Message = "Empty Request"
};
if (string.IsNullOrEmpty(request.Username) || string.IsNullOrEmpty(request.Password))
return new LoginResponse
{
Success = false,
Message = "Username and/or password empty"
};
// This is returning null since dataAccess is a mock
return dataAccess.Login(request);
}
You said you think you're doing a lot wrong, but the way you have the test set up is pretty much what I do. The only thing I would change (in addition to fixing the Setup method as described above) is to use the UnitOfWork_StateUnderTest_ExpectedBehavior naming pattern for your test. For example Login_ValidLoginRequest_ShouldReturnValidLoginResponse()

The problem with this code
var result = logic.Login(new LoginRequest { Password = "dummy", Username = "dummy", IPAddress = "1.1.1.1" })
is that in the implementation of the Login method this is called
dataAccess.Login(request)
It means you have to setup the mock of DataAccess for method Login because mock does otherwise nothing. Mock if fake and needs to be setup so it works the way you need. In this case the answer of #Ben Rubin is absolutely correct.
When mock is setup like this
MockedDataAccess.Setup(x => x.Login(request)).Returns(response)
then it is necessary to call the method under test with exactly the same request object as the request which was used in setup of data access Login method, because otherwise mock will act as not setup. Here you basically saying 'when DataAccess Login is called with exactly this request, that response will be returned'.
But when mock is setup like this
MockedDataAccess.Setup(x => x.Login(It.IsAny<LoginRequest>())).Returns(response)
then it works, because here Login is setup to any LoginRequest. So the mock will in this case return response no matter what request was used. HTH
Here are more information about Mock Matching Arguments

Related

How to capture current Status code in ASP.NET CORE 3.1 Web API

I have written method that returns the result in the object. I need to capture current HttpStatusCode rather than I create my own and save in the following object before returning a result
[HttpPost]
public ActionResult CreateUser([FromBody]UsereDto user)
{
try
{
var data = UserService.CreateUser(user);
var result = new ResultDto
{
Ok = true,
Data = data,
Error = "No",
StatusCode = HttpStatusCode. ???????????????
};
return Ok(result);
}
catch(Exception exp)
{
var errorResult = new ResultDto
{
Ok = false,
Data = null,
Error = exp.ToString()
StatusCode = ????????????
};
return Ok(errorResult);
}
}
Can't capture existing http code
you can manually enter http codes yourself.
For Example:
Created Code=201
Error Code =400
BadRequest:301
NotFound:404
return StatusCode(200,object);
You can also return it this way.
I am not 100% sure what do you want to achieve or why do you even want that since HTTP Response already will contain an http status, so returning it additionally in a JSON response does not add more information than is already available.
But you can just hardcode the status since it is always 200 in your example, unless there is some error during serialization of the response, etc.
Right after your ??? line you do return an Ok() response that will return 200.
You don't need to create your own status code inside dto...
return Ok(errorResult); will return 200 with error and this is bad.
Instead, you should return BadRequestResult(errorResult) and ui will receive 500 result containing your DTO.
Also, instead of return Ok(result);, you can return OkResult(result) and this will return 200 to UI, also containing your DTO...
Also, you should extract Dto creation to it's own method in separate class, to follow DRY and single responsibility principles..

How can I setup IHttpClientAdapter.GetAsync to return a HttpResponseMessage with particular value for IsSuccessStatusCode?

I have a class that has a dependency on the IHttpClientAdapter, which I am using to call a RESTful API.
I have a test for the method that uses the dependency, so I am mocking the IHttpClientAdapter using Moq. The test should check that when the GetAsync method returns any unsuccessful response, something happens.
One thing that I could do is create a new HttpResponseMessage like so:
var response = new HttpResponseMessage
{
StatusCode = HttpStatusCode.InternalServerError
};
Then setup the mocked dependency to return that like so:
_httpClientAdapterMock.Setup(x => x.GetAsync(new Uri("http://someuri"))).ReturnsAsync(response);
IsSuccessfulStatusCode in this case would evaluate to false, so the test passes as one would expect.
However, this test is not as robust as I'd like it to be, as it only checks one particular response code. What I would really like to do is set the IsSuccessfulStatusCode to false instead. So something like this:
var response = new HttpResponseMessage
{
IsSuccessStatusCode = false // cannot do this, as IsSuccessStatusCode is readonly
};
I have tried extending the HttpResponseMessage (named FakeHttpResponseMessage), creating an instance of that class and setting the IsSuccessStatusCode property to true, and then setup the mock to return that instead, but the IsSuccessStatusCode property evaluates to true when running the test, so it didn't have the desired effect.
How can this be achieved, without having to test each and every possible unsuccessful HttpStatusCode?
It's probably going to be something like Set the response.status = 200 because 200 is the response code for success.

How to unit test webapi controller sends back error

I have the following controller method:
public IHttpActionResult GetAttractions([FromUri] SearchAttractionRequest request)
{
try
{
var requestParameterType = request.RequestType();
var searchResultModel = _attractionService.GetAttractions(request);
return TransformListToResult(searchResultModel.Results, request.PerPage, searchResultModel.TotalItemCount, searchResultModel.GeneratedQuery);
}
catch (Exception ex)
{
// return Content(HttpStatusCode.BadRequest, "Any object");
// return ResponseMessage(Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex.Message));
}
}
When making the call request.RequestType() if the parameter passed in is invalid an argument exception will be thrown and code will end up in catch block. I have a unit test that I am trying to write:
[Test]
public void Given_Mixed_Parameters_Expect_Error()
{
var invalidQuery = new SearchAttractionRequest
{
Category = Guid.NewGuid().ToString() + "|a catgeory|" + Guid.NewGuid().ToString(),
Genre = "",
Region = ""
};
var attractionServiceMock = Mock.Of<IAttractionService>();
var sut = new AttractionsApiController(attractionServiceMock);
Action act = () => sut.GetAttractions(invalidQuery);
act.ShouldThrow<HttpResponseException>();
}
Basically I want to test that when I send in invalid param I get back error response. So what do i need to send back from my api method and what do i need todo in my test to test for the error. At the moment I am getting error in my test becuase the expected error is argument exception, which is correct but i want to mimic what a client would get back.
Do I need todo something like this in my test:
as OkNegotiatedContentResult<SomeObject>;
You can't exactly write a test for the controller that will mimic what the client receives. That's because, by design, the controller is just a class with methods that return some result. Technically it doesn't determine what the client receives.
For example, if you call a method that throws an unhandled ArgumentException, other middleware is going to determine that the client should receive a 500 response. The controller isn't doing that.
Or, a controller might return a ViewResult. The client doesn't get a ViewResult - it gets HTML. The controller doesn't determine what that HTML will be.
When you're writing unit tests for the controller, think of it as just another class. You're just testing that it returns what it's supposed to.
If the controller throws an unhandled ArgumentException you can test for that.
If you handle the ArgumentException and return
Content(HttpStatusCode.BadRequest, "Any object");`
You could do this:
var result = (NegotiatedContentResult<string>)controller.GetResult();
Assert.AreEqual(HttpStatusCode.BadRequest, result.StatusCode);
I believe you could use the following:
var attractionServiceMock = Mock.Of<IAttractionService>();
var sut = new AttractionsApiController(attractionServiceMock);
var result = sut.GetAttractions(invalidQuery) as BadRequestErrorMessageResult;
Assert.IsNotNull(result);

Unit testing IAuthenticationFilter in WebApi 2

I'm trying to unit test a basic authentication filter I've written for a WebApi 2 project, but i'm having trouble mocking the HttpAuthenticationContext object required in the OnAuthentication call.
public override void OnAuthentication(HttpAuthenticationContext context)
{
base.OnAuthentication(context);
var authHeader = context.Request.Headers.Authorization;
... the rest of my code here
}
The line in the implementation that I'm trying to set up for mocking is the one that sets the authHeader variable.
However, I can't mock the Headers object because its sealed. And I can't mock the request and set a mocked headers because its a non-virtual property. And so on up the chain all the way to the context.
Has anyone successfully unit tested a new IAuthenticationFilter implementation?
I'm using Moq but I'm sure I could follow along in any mocking library if you have sample code.
Thanks for any help.
It is possible to achieve what you wanted however as none of the objects in the chain context.Request.Headers.Authorization exposes virtual properties Mock or any other framework won't provide much help for you. Here is the code for obtaining HttpAuthenticationContext with mocked values:
HttpRequestMessage request = new HttpRequestMessage();
HttpControllerContext controllerContext = new HttpControllerContext();
controllerContext.Request = request;
HttpActionContext context = new HttpActionContext();
context.ControllerContext = controllerContext;
HttpAuthenticationContext m = new HttpAuthenticationContext(context, null);
HttpRequestHeaders headers = request.Headers;
AuthenticationHeaderValue authorization = new AuthenticationHeaderValue("scheme");
headers.Authorization = authorization;
You just simply need to create in ordinary fashion certain objects and pass them to other with constructors or properties. The reason why I created HttpControllerContext and HttpActionContext instances is because HttpAuthenticationContext.Request property has only get part - its value may be set through HttpControllerContext. Using the method above you might test your filter, however you cannot verify in the test if the certain properties of objects above where touched simply because they are not overridable - without that there is no possibility to track this.
I was able to use the answer from #mr100 to get me started in solving my problem which was unit testing a couple of IAuthorizationFilter implementations. In order to effectively unit test web api authorization you can't really use AuthorizationFilterAttribute and you have to apply a global filter that check for the presence of passive attributes on controllers/actions. Long story short, I expanded on the answer from #mr100 to include mocks for the controller/action descriptors that let you test with/without the presence of your attributes. By way of example I will include the simpler of the two filters I needed to unit test which forces HTTPS connections for specified controllers/actions (or globally if you want):
This is the attribute that is applied where ever you want to force an HTTPS connection, note that it doesn't do anything (it's passive):
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class HttpsRequiredAttribute : Attribute
{
public HttpsRequiredAttribute () { }
}
This is the filter that on every request checks to see if the attribute is present and if the connection is over HTTPS or not:
public class HttpsFilter : IAuthorizationFilter
{
public bool AllowMultiple => false;
public Task<HttpResponseMessage> ExecuteAuthorizationFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
List<HttpsRequiredAttribute> action = actionContext.ActionDescriptor.GetCustomAttributes<HttpsRequiredAttribute>().ToList();
List<HttpsRequiredAttribute> controller = actionContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<HttpsRequiredAttribute>().ToList();
// if neither the controller or action have the HttpsRequiredAttribute then don't bother checking if connection is HTTPS
if (!action.Any() && !controller.Any())
return continuation();
// if HTTPS is required but the connection is not HTTPS return a 403 forbidden
if (!string.Equals(actionContext.Request.RequestUri.Scheme, "https", StringComparison.OrdinalIgnoreCase))
{
return Task.Factory.StartNew(() => new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden)
{
ReasonPhrase = "Https Required",
Content = new StringContent("Https Required")
});
}
return continuation();
}
}
And finally a test to prove it returns a status of 403 forbidden when https is required but not used (using a lot of #mr100's answer here):
[TestMethod]
public void HttpsFilter_Forbidden403_WithHttpWhenHttpsIsRequiredByAction()
{
HttpRequestMessage requestMessage = new HttpRequestMessage();
requestMessage.SetRequestContext(new HttpRequestContext());
requestMessage.RequestUri = new Uri("http://www.some-uri.com"); // note the http here (not https)
HttpControllerContext controllerContext = new HttpControllerContext();
controllerContext.Request = requestMessage;
Mock<HttpControllerDescriptor> controllerDescriptor = new Mock<HttpControllerDescriptor>();
controllerDescriptor.Setup(m => m.GetCustomAttributes<HttpsRequiredAttribute>()).Returns(new Collection<HttpsRequiredAttribute>()); // empty collection for controller
Mock<HttpActionDescriptor> actionDescriptor = new Mock<HttpActionDescriptor>();
actionDescriptor.Setup(m => m.GetCustomAttributes<HttpsRequiredAttribute>()).Returns(new Collection<HttpsRequiredAttribute>() { new HttpsRequiredAttribute() }); // collection has one attribute for action
actionDescriptor.Object.ControllerDescriptor = controllerDescriptor.Object;
HttpActionContext actionContext = new HttpActionContext();
actionContext.ControllerContext = controllerContext;
actionContext.ActionDescriptor = actionDescriptor.Object;
HttpAuthenticationContext authContext = new HttpAuthenticationContext(actionContext, null);
Func<Task<HttpResponseMessage>> continuation = () => Task.Factory.StartNew(() => new HttpResponseMessage() { StatusCode = HttpStatusCode.OK });
HttpsFilter filter = new HttpsFilter();
HttpResponseMessage response = filter.ExecuteAuthorizationFilterAsync(actionContext, new CancellationTokenSource().Token, continuation).Result;
Assert.AreEqual(HttpStatusCode.Forbidden, response.StatusCode);
}

C#: Web Service Login is working, but another method is giving me an error

I have the 2 methods below, in a class, The Login Method works fine and retrieves and sets the session token, but in order for me to use GetEvents(), I have to send the sessionToken in the request for GetEvents().
But on the 4th Line of code (excluding Comments & Whitespace) of the getEvents(), I get the error: Object reference not set to an instance of an object.
The Entire Source can be downloaded here: (Copy and Paste into your browser)
http://www.theebookzone.co.uk/betfairui.zip
Any Ideas what Im doing wrong?
Any help appreciated, even if its not directly related to this matter.
public static string SessionToken = ""; // Set by Login();
static LoginResp Login()
{
// Make a new BFGS instance
BFGlobal = new BFGlobalService.BFGlobalService();
// Set up the request in [req]
LoginReq req = new LoginReq();
req.username = username;
req.password = password;
req.productId = productId;
req.vendorSoftwareId = softwareId;
// Set up the response in [resp]
// Execute the call, and pass in the request
LoginResp resp = BFGlobal.login(req);
// Sets our public variable above to the recieved sessionToken
SessionToken = resp.header.sessionToken;
// return [resp] - which is the response from the call
return resp;
}
public Array GetEvents()
{
// This will set the sessionToken declared at the top.
LoginToBetfair();
// Make a new instance of the web service
BFGlobal = new BFGlobalService.BFGlobalService();
// Load up the request
GetEventsReq req = new GetEventsReq();
// Error Line Below:
req.header.sessionToken = SessionToken; // <--- Here is where I get the error
// Error Above Line: Object reference not set to an instance of an object.
GetEventsResp resp = BFGlobal.getEvents(req);
Array marketItems = resp.marketItems;
return marketItems;
}
Web Services are usually stateless. In order to store a session, you should cache it using the HTTPContext.
I'd wager that the null object is the header of req.header. Put a break point at that line and then in the variables debugger window see what req.header evaluates to. If it is indeed null then you'll have to manually add the headers with something like
req.headers = new Headers();

Categories