how to unittest an asp.net web api? - c#

I never did unittesting for an asp.net web api. The service is about posting a JSON in the request and returning a JSON result after JSON schemavalidation and querying. Just wondering how to unittest a web api in general. An example of a test would be posting an invalid JSON string and testing whether this returns the correct HTTPcode ie 400 or something. This is roughly what my service looks like:
public class MyApiController : ApiController
{
public HttpResponseMessage Post([FromBody]dynamic value)
{
return response;
}
}
Also how can I use constructor injection with web apis? If I use a constructor here my value that is posted is null?

You can directly create the instance of controller. If you are used any complicated code, you can create mock for the class for unit testing. Refer this link to understand unit testing of ASP.NET web API.

[Test]
public async void GetSettingsRequest()
{
var getSettingsResponse = await api.GetSettings();
Assert.IsNotNull (getSettingsResponse);
Assert.IsTrue (getSettingsResponse.Success);
Assert.IsNotNullOrEmpty (getSettingsResponse.Email);
}
I made test project which is making request on api server. After request is made, wait for response and test response.

My first thought is that you should figure out what you want to test first. That means just jotting down all of the expected behaviors, then writing tests to ensure that your controller behaves as... expected.
Let's start with two fictional test cases:
The response is not null
The response always carries a success status code
--
class MyApiControllerTest
{
private MyApiController myApiControllerFixture;
[TestInitialize]
void Initialize()
{
this.myApiControllerFixture = new MyApiController();
}
[TestMethod]
void MyApiController_Post_ResponseNotNullTest()
{
var response = this.myApiControllerFixture.Post(new { });
Assert.IsNotNull(response);
}
[TestMethod]
void MyApiController_Post_SuccessStatusCodeTest()
{
var response = this.myApiControllerFixture.Post(new { });
Assert.IsTrue(response.IsSuccessStatusCode);
}
}
Just add tests as you discover new required behaviors, and delete obsolete tests when behaviors change.

Related

.NET Core TestServer returns status 200 instead of 404 for post request to non-existent path

I am implementing integration tests for a .NET Core 3.1 application using the following guide: https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-3.1. This is my current test class:
public class IntegrationTests : IClassFixture<WebApplicationFactory<main_app.Startup>>
{
private readonly WebApplicationFactory<main_app.Startup> _factory;
public TestTest(WebApplicationFactory<main_app.Startup> factory) {
_factory = factory;
}
[Fact]
public async void GetBogusPath() {
var client = _factory.CreateClient();
var response = await client.GetAsync("ofajspd");
Assert.False(response.IsSuccessStatusCode);
}
[Fact]
public async void PostBogusPath() {
var client = _factory.CreateClient();
var request_body = new StringContent("{}", Encoding.UTF8, "application/json");
var response = await client.PostAsync("ofajspd", request_body);
Assert.False(response.IsSuccessStatusCode);
}
}
The get request test acts as expected, in that it returns a 404 status code. However, the post request returns a 200 status, even though the path doesn't exist. What could be causing this problem, and how do I resolve it?
If I run the server locally and fire post requests at a non-existent endpoint manually, it acts as expected, so the problem is limited to just these integration tests.
EDIT: Here's my controller class definition:
[ApiController]
[ApiVersion("1")]
[Route("v{version:apiVersion}/[controller]")]
public class QueryController : ControllerBase
{
[AcceptVerbs("Post")]
public async Task<QueryResponse> Post([FromBody]QueryRequest rq) {
return await QueryHandler.HandlePost(rq, this.HttpContext);
}
}
The problem was that I had attached a custom piece of middleware in Startup.cs that had a bug, so it was swallowing 404s generated by other middleware and outputting 200s instead. It was supposed to be handling a specific type of error, but the bug caused it to handle a much wider array of errors than intended. Removing the faulty middleware fixed the problem.
For extra detail, the custom middleware in question was checking for a specific 'HResult' in exceptions thrown by other middleware layers, but the HResult value referred to an AggregateException that was thrown much more often than the specific error I was looking for (which was writing to a response after it had already begun streaming).

Testing API with PostAsync and Database

I wrote a REST API in C# that uses a business logic and some data access to work with a database.
Now I want to test the API using PostAsync, GetAsync and DeleteAsync Methods in the xUnit Framework.
But I do not want to use my actual database, but a testdatbase.
The problem is that I need to check the response body and the status code and therefore need to use the data access and a test database.
I simply don't know where to start. I believe I have to implement some kind of interface that uses a testdatabase and some response handler for my unit tests.
I can't find any tutorials that involve both, a test datbase for unit tests and a real database for the program.
Any idea how I can handle this issue?
Hit me up, if I should provide some code.
EDIT: here is some code that might explain the issue
I want my tests to look something like this and to return the list of items
[Fact]
public async Task GetAllItemsTest()
{
HttpClient Client = new HttpClient();
HttpResponseMessage message = await Client.GetAsync("https://localhost:xxxxx/api/AllItems") as List<Item>;
Assert.True(Check if list contains the expexted number of elements)
Assert.Equal("OK",message.StatusCode.ToString());
}
}
Also I need to check the database when testing my POST methods.
Just so I can see that an entry was added.
Maybe something like this:
[Fact]
public async Task AddNewItemTest()
{
HttpClient Client = new HttpClient();
HttpContent content = new HttpContent {itemName = "NewItem"};
HttpResponseMessage message = await Client.PostAsync("https://localhost:xxxxx/api/NewItem", content);
Assert.True(Check if entry to database was made)
Assert.Equal("OK",message.StatusCode.ToString());
}
}
Data access consist of a controller that implements a constructor for my Database context. Basically all data access methods look like GetItems().
I use EF Core.
public class ItemsController : ControllerBase
{
//Constructor for DB usage
private DataContext _context;
public ItemsController(DataContext context)
{
_context = context;
}
// Get all Items
public List<Items> GetItems()
{
return _context.Items.ToList();
}
// Add new Item
public void AddItem(Item item)
{
_context.Items.Add(item);
_context.SaveChanges();
}

Flurl Testing: How to setup response based on conditions?

I have a method that makes two HTTP calls to two different urls using Flurl. I need to unit test this method in which I want Flurl to respond with two different responses. How can I set this up?
My test class is like:
public class SUT
{
public async Task Mut(object obj)
{
var x = await url1.PostJsonAsync(obj).ReceiveJson();
if ((bool)x.property = true)
{
var y = await url2.GetJsonAsync();
// Process y.
}
}
}
I have a test class as below:
public class TestSut : Disposable
{
private readonly HttpTest httpTest;
public TestSut()
{
httpTest = new HttpTest();
}
[Fact]
public async Task TestMut()
{
// call Mut...
}
public void Dispose()
{
httpTest?.Dispose();
}
}
What I would like is something along the lines of:
httpTest.ForUrl(url1).ResponsdWithJson(...);
httpTest.ForUrl(url2).ResponsdWithJson(...);
The short answer is no, you can't configure different behavior by URL today, but it's coming in 3.0. I'll update this when it's released (or hopefully someone else will if I forget :).
In this particular case though, assuming your SUT code at least somewhat resembles the real code you're targeting, it looks like url1 will always be called before url2, so if you just queue the responses in the same order, Flurl will guarantee that they are returned in the same order.
httpTest
.ResponsdWithJson(/* fake response for url1 */)
.ResponsdWithJson(/* fake response for url2 */);
Of course the SUT may not actually call things in a determinate order like that, in which case you'll have to wait for 3.0 unfortunately.

Hierarchical "OneTimeSetUp" methods

Ok, I've got some nunit tests I'm writing to test an API. Any time I need to run these tests, I first need to login to the api to obtain a token. To start with, that's how I've written my OneTimeSetUp.
So, OneTimeSetUp is called, I log in, a shared field stores the token, each test is called a tests a different endpoint on api.
Now the problem. We've decided that we want to have individual tests for individual fields on the response, so that we can see what exactly is (and isn't failing) if something is wrong. So, we split out each endpoint into it's own test.
Now, OneTimeSetUp is called, it logs in, and calls the endpoint, stores the result, and all the tests fire, testing their little bit.
The problem is, logging in takes time, and there is no logical reason why all the separate tests couldn't just use the same login details. Is there any way of further sub-dividing tests/ adding extra levels of test? It would be great if we could get a test result that looks like this
ApiTests <--- shared sign-in at this level
- Endpoint 1 <--- call the endpoint at this level
- Field 1 \
- Field 2 --- individual test results here
- Field 3 /
- Endpoint 2 <--- call the endpoint at this level
- Field a \
- Field b --- individual test results here
- Field c /
You can group your test classes into the same namespaces and then add an additional class that is marked with the SetupFixture attribute. This will run the initialization code only once per namespace. (Not to be confused with the "TestFixtureSetUp" attribute, which is marked obsolete since NUnit v3. Thanks Charlie for your comment, I initially mixed it up.)
https://github.com/nunit/docs/wiki/SetUpFixture-Attribute
Code sample (as always, you are free to put each class into a separate code file):
using System.Diagnostics;
using NUnit.Framework;
namespace Test
{
[SetUpFixture]
public class SharedActions
{
[OneTimeSetUp]
public void SharedSignIn()
{
Debug.WriteLine("Signed in.");
}
[OneTimeTearDown]
public void SharedSignOut()
{
Debug.WriteLine("Signed out.");
}
}
[TestFixture]
public class FirstEndpointTests
{
[Test]
public void FirstEndpointTest()
{
Debug.WriteLine("Test for Endpoint A");
}
}
[TestFixture]
public class SecondEndpointTests
{
[Test]
public void SecondEndpointTest()
{
Debug.WriteLine("Test for Endpoint B");
}
}
}
When you "debug all" tests, the following output will appear in the debug window:
Signed in.
Test for Endpoint A
Test for Endpoint B
Signed out.
Here is one possible way of achieving this.
If you have a common base class (as it sounds from your description), you can create a protected lazy to get your token as per the example below
public class ApiTestsBase
{
protected static Lazy<string> TokenLazy = new Lazy<string>(() =>
{
// Log in and get your API token
Console.WriteLine("Logging into API to get token. You should only see this message on the first test that runs");
return "DEADBEEF";
});
}
[TestFixture]
public class EndpointATests : ApiTestsBase
{
private string GetResultFromEndPoint()
{
// Call endpoint with token from TokenLazy.Value
Console.WriteLine($"Calling EndpointA with token {TokenLazy.Value}");
return "PayloadA";
}
[Test]
public void Test1()
{
var payload = this.GetResultFromEndPoint();
// Assert things about payload
}
}
[TestFixture]
public class EndpointBTests : ApiTestsBase
{
private string GetResultFromEndPoint()
{
// Call endpoint with token from TokenLazy.Value
Console.WriteLine($"Calling EndpointB with token {TokenLazy.Value}");
return "PayloadB";
}
[Test]
public void Test1()
{
var payload = this.GetResultFromEndPoint();
// Assert things about payload
}
}
Now I am using string types, but you can use whatever request, response and token types are relevant to your situation. I suspect that you could also with a bit of creativity move the GetResultFromEndPoint call to the base class and use abstract methods or properties to fill in the endpoint specific detail, but you have not shared enough code for me to try that.
The magic is in the static keyword which means you will only have one instance per app domain. The Lazy simply defers creation until its first reference. It gets a little more complex if your test cases run for a long time because you will need to deal with token renewal, but it can still be achieved in a similar way using a singleton class that periodically re authenticates if token age > x. A singleton object can also be used in place of the static in the above example if you do not have a common base class for your fixtures.

Unit test ServiceStack services in ServiceStack 3.9.71

I recently took a .Net project over which exposes DAOs from a Microsoft SQL Database via ServiceStack(3.9.71) REST API. Since I am gonna refactor some parts I want to unit test (at least) all servicestack services. For a better understanding I quickly draft how the implementation works.
Each Service contains a property of type DBService which encapsulates all database accesses of all services. Unfortunately this is a concrete class which makes it hard to mock. The DI.Container wrappes ServiceStack's IOC.
public class SomeService : Service
{
public DBService { get { return DI.Container.Resolve<DBService>(); } }
public object Get(SomeDataClass class)
{
var response = DBService.SomeServiceGet();
return response;
}
// other code omitted
}
The DBService looks like this (draft):
public class DBService
{
public IDbConnectionFactory DBFactory { get { return DI.Container.Resolve<IDbConnectionFactory>(); } }
public SomeServiceResponse SomeServiceGet()
{
//DB Access here...
// ...
}
public SomeOtherServiceResponse SomeOtherServiceGet()
{
//...
}
// following about 30 other methods for all the services (POST,GET,PUT etc)
}
I read the detailed response to this question but I was not able to create and initialize a BasicAppHost in ServiceStack 3.9.71 since it immediately threw a System.TypeLoadExceptionMethod 'get_VirtualPathProvider'.
On the other hand I was thinking that I do not actually need a BasicAppHost. I just have to unit test the DBService and then the servicestack services with a somehow mocked DBService. The only problem I have is that DBService is not an interface and that I am actually not sure how to deal (mock) with the SQL database and the IOC.
[UPDATE]
Unfortunately I am still not able to test a service since I can not just new the service in my test. If I do so I get:
System.TypeLoadExceptionCould not load type 'ServiceStack.ServiceHost.IService' from assembly
Here is my test:
[Fact]
public void SomeDataTest()
{
var serviceUnderTest = new SomeService();
var response = serviceUnderTest.Get(new SomeDataClass());
Assert.NotNull(response);
}
I guess the problem is that the services strongly uses alot of properties which are injected via the IOC. How can I mock that? Creating a BasicAppHost and retrieving the service from there also does not work which I already mentioned.
If you are just testing your service class, then you can directly mock any dependencies:
[Fact]
public void SomeDataTest(
{
var serviceUnderTest = new SomeService();
var logger = new Mock<ILogger>(); // Rhino mocks fashion.
serviceUnderTest.Logger = logger.Object;
var response = serviceUnderTest.Get(new SomeDataClass());
Assert.NotNull(response);
}
There's an page in their older docs here about integration testing in case you want to test the AppHost
Edit: there's an example of mocking the service dependencies here.

Categories