Testing API with PostAsync and Database - c#

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();
}

Related

Sharing HTTP API interface between Server and Client (C# Asp Core) and validating at compile time

I'm building an application which uses ASP Core Blazor WebAssembly and ASP Core MVC for the backend. This allows for a lot of code re-use, however I'm a bit lost when it comes to sharing API specifics (routes, HTTP method and return values). gRPC solves this perfectly but has some drawbacks. Is there anyway to solve this with pure C#?
Consider this following basic controller:
[ApiController]
[Route("[controller]")]
public class PostController : ControllerBase
{
private readonly ILogger<PostController> logger;
private ApplicationDbContext context;
public PostController(ILogger<PostController> logger, ApplicationDbContext dbContext)
{
this.logger = logger;
context = dbContext;
}
[HttpGet("list")]
[AllowAnonymous]
public async Task<ActionResult<IEnumerable<SelectListOption<int>>>> List()
{
var posts = await context.Posts.ToListAsync();
return posts.OrderBy(e => e.Name == "Other").ThenBy(e => e.Name).Select(e => new SelectListOption<int> { Id = e.Id, Name = e.Name });
}
}
On the WebAssembly (client) side, I have a basic class which calls the corresponding Apis:
public class PostClient : IPostClient
{
private readonly HttpClient client;
private const string endpoint = "Post";
public PostClient(HttpClient client)
{
this.client = client;
}
public async Task<IList<SelectListOption<int>>> ListPostsAsync()
{
try
{
var result = await client.GetFromJsonAsync<List<SelectListOption<int>>>($"{endpoint}/list");
if (result == null) throw new Exception();
return result;
}
catch (AccessTokenNotAvailableException exception)
{
exception.Redirect();
}
return new List<SelectListOption<int>>();
}
}
There are a couple of problems with this:
The string "Post" is hardcoded. There is no guarantee that this controller exists on the backend. Similarly, the path for the specific action (/lists) is also hardcoded. There is no guarantee this path is actually defined and would not be apparent until runtime. I'm looking for a solution that can guarantee that all api paths in the client are defined correctly on the backend during compile-time, run-time (during startup), or in unit tests.
My first thought was adding an interface (in a shared assembly) which is implemented by PostController, it would just define the endpoint and return values.
interface IPostApi
{
Task<IEnumerable<SelectListOption<int>>> List();
}
class PostController : IPostApi
{
//...
}
However this does not work because PostController.List needs to implement ActionResult which is not compatible with IPostApi which does not have ActionResult.
It seems like the only way to do this is to use integration tests or other tests which iterate through all the clients and apis and verify the routes and data types match up. Is there anyway to have this sort of stuff checked at compile time?
There is not. APIs are in their nature http and independent from the client side.
Be that as it may, a good way to do this, is to generate the open api json file, and auto generate the client side https://learn.microsoft.com/en-us/aspnet/core/tutorials/web-api-help-pages-using-swagger?view=aspnetcore-3.1. Check https://github.com/OpenAPITools/openapi-generator for a library to do that.
This is similar to wsdl for soap services.

Integration tests with asp.net core (test of controllers without the views)

I am trying to setup a test project to test my controllers with identity and the database without having to define the views.
I have a unit test project where I can test my controller by instanciating it, passing the dbContext to the constructor.
public class EventControllerTests
{
private readonly IEventRepository _eventRepository;
private readonly EventController _controller;
private readonly AppDbContext dbContext;
const string cn = "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=EventDb;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False";
public EventControllerTests()
{
var options = new DbContextOptionsBuilder<EVNTS.Web.Database.AppDbContext>()
.UseSqlServer(cn).Options;
dbContext = new EVNTS.Web.Database.AppDbContext(options);
// Arrange
_eventRepository = new EventRepository(dbContext);
_controller = new EVNTS.Web.Controllers.EventController(_eventRepository);
}
[Fact]
public void ActionIndexTest()
{
// Act
var result = _controller.Index(1);
// Assert
var model = (Event)result.Model;
Assert.Equal(1, model.Id);
}
}
I have an integration test project where I use a WebApplicationFactory
public class BasicTests : IClassFixture<WebApplicationFactory<EVNTS.Startup>>
{
private readonly WebApplicationFactory<EVNTS.Startup> _factory;
private readonly HttpClient _client;
public BasicTests(WebApplicationFactory<EVNTS.Startup> factory)
{
_factory = factory;
_client = _factory.CreateClient();
}
[Theory]
[InlineData("/")]
public async Task Get_EndpointsReturnSuccessAndCorrectContentType(string url)
{
// Act
var response = await _client.GetAsync(url);
// Assert
response.EnsureSuccessStatusCode(); // Status Code 200-299
Assert.Equal("text/html; charset=utf-8",
response.Content.Headers.ContentType.ToString());
}
[Fact]
public async Task TestUserRegistration()
{
var s = _factory.Services.GetRequiredService<EVNTS.Web.Repositories.IEventRepository>();
var url = "/user/register";
var inputModel = new EVNTS.Web.ViewModels.RegisterModel()
{
UserName = "eric",
Password = "123456",
ConfirmPassword = "123456"
};
var sObj = JsonSerializer.Serialize(inputModel);
var content = new StringContent(sObj, Encoding.UTF8, "application/json");
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await _client.PostAsync(url, content);
var result = response.Content.ReadAsStringAsync();
}
}
The problem is that with the second option, the views have to be created and I need to use a library like AngleSharp to test the results.
I would like something in between where I can call the contructor directly and test the result view but with the DI injecting the UserManager and the dbContext for me.
any ideas?
Cheers
Here is the controller:
public class UserController : Controller
{
private readonly UserManager<User> _userManager;
public UserController(UserManager<User> userManager)
{
_userManager = userManager;
}
[HttpPost]
public async Task<IActionResult> Register([FromBody] RegisterModel model)
{
IdentityResult? result=null;
if (ModelState.IsValid)
{
var user = await _userManager.FindByNameAsync(model.UserName);
if (user == null)
{
user = new User
{
Id = Guid.NewGuid(),
UserName = model.UserName,
};
result = await _userManager.CreateAsync(user, model.Password);
}
}
return View(result);
}
}
I also find this usefull sometimes when you want to check the result of a controller in an integration test condition without checking the view.
You can use the dependency injection and create a scope from the WebApplicationFactory.
using (var serviceScope = Factory.Services.CreateScope())
{
var sut= serviceScope.ServiceProvider.GetService<YourController>();
}
To make this work you have to call the method AddControllersAsServices() in Startup.cs to register the controller in the DI container
services.AddControllersWithViews(options => { options.ConfigureMvcOptionsForPortalModule(); })
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
.AddControllersAsServices();//add controller in DI to access it in integration testing
There is no in between. The first example is a unit test, while the second is an integration test. If you want to just look at the result object of the action, then you'd use the unit test methodology, and you'd need to mock out your dependencies. Otherwise, you'd use the integration test approach, and you have to deal with the actual simulated server response.
For what it's worth here, controller actions should be integration tested, since they are inherently dependent on a number of components coming together, so you should be following the second approach, parsing the HTML response, if necessary.
I don't consider myself an authority on how to perform unit testing, but since the comment section is restricted in characters I will write my comments here.
Usually, when you find yourself in a situation where it's difficult to come up with a good unit test (I won't define "good" here) more often than not, it is because there are some problems with the project structure/code design, and not actual limitations of the unit testing itself (again, not that unit testing doesn't have it's limitations, but I think this is not the case here).
Based on the above I asked you to include the action's code so we can examine what exactly are you trying to test and why it is so hard.
Here comes the heavily opinion-based part of my comment, but I leave it up to you wether you would want to take some of this or leave it.
It's not a rule, but a good rule of thumb is that the controller should contain very little business logic, which means that unit testing a controller should be basically testing the different paths that the request could go, once it hits the controller.
Generally you would want something like this:
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = await _userManager.FindByNameAsync(model.UserName);
...
return View(result);
which then you can unit test with something like this:
public async Task Register_Returns_BadRequest_On_Invalid_Model()
{
var testUsername = "TestUsername";
var mockUserManager = new Mock<IUserManager>();
mockUserManager.Setup(m => m.FindByNameAsync(testUsername))
.Returns(Task.FromResult(**Not sure about this part**))
var controller = new RegisterController(mockUserManager.Object);
var result = await controller.Register(model: null);
var actionResult = Assert.IsType<ActionResult<IdentityResult>>(result);
Assert.IsType<BadRequestObjectResult>(actionResult.Result);
}
For the happy path you want only to check that on a valid ModelState the result is of type ActionResult>
What is my idea:
When you unit test the controller you should not be bothered by the actual data, this is responsibility of other parts of the application
The controller unit test should be plain simple, most of the time you should be testing only those two cases - invalid data returns some sort of BadRequest, valid data returns the expected response
If you find yourself mocking too much objects most of the time it's a clear sign that you need some additional layer of abstraction.
In your case, in order to make my code better structured and easier for testing I would do the following:
First test for invalid ModelState - you don't want to proceed if the ModelState is invalid and this should also be covered by an unit test.
Managers should be a higher level of abstraction. Methods like FindByNameAsync and CreateAsync are more suitable for the data access layer. In the case of this action, your UserManager can have a method like Register so your controller's action look like this:
if (!ModelState.IsValid)
{
return BadRequest()
}
var result = _userManager.Register(model.UserName);
return View(result);
Now you can remove the Find and Create methods from the controller and create a UserRepository where I thin those methods belong and where you can test them in isolation.
In this setup You have these abstractions Controller -> Manager -> Repository. Now you try to test the three of these in one single method, which is causing the problems in my opinion.
Also, just because I find this a bit more tidy, usually you use a Service layer and if the structure is too complex you add the manager layer so it becomes Controller -> Manager -> Service -> Repository. In your case I'm not sure that you need this complexity so maybe just for the sake of better naming, rename the UserManager to UserService, so that your code flow is Controller -> Service -> Repository.
Also, last pease of advice. Controller testing has always been contraversial so don't be too bothered if you don't cover your controller with unit tests as much as other parts of the code. This is somewhat expected, what I wanted to tell with this post is mainly that the problem wasn't how to test but rather is the code testable as it is, which in my opinion could be improved as I've shown above. Yes, my proposition is not perfect as well but it creates smaller chunks of code which are encapsulated, don't have that many dependencies which ultimately makes them easier to test. And of course this is not a substitution of the integrations tests.
Hope this gave you some food for thought.

Is it viable/thread-safe to use a static HttpClient in a using block?

Say I have a controller with an Index() method, and this controller utilizes multiple "Manager classes" that manage certain assets that need to be retrieved with an HttpClient from an API.
I've read that sharing an HttpClient with multiple calls is better than to reinstantiate it with every call to save ports.
I do however want to dispose of the HttpClient before the controller returns the view, because the view contains an entire Knockout/Typescript based front end project that handles the rest of the data (so it's basically only settings and meta data stuff).
Do I need to pass the HttpClient variable to each and every "Manager class", or does it suffice to do something like the following, and use a static HttpClient inside the classes?
public ActionResult Index()
{
using (Globals.Client = new System.Net.Http.HttpClient())
{
// do stuff like SettingManager.GetSetting("settingKey") which uses
// the Globals.Client variable
}
return View();
}
Or should I not even want to dispose the HttpClient in the first place?
One solution is to make a separate dependency responsible for managing your HttpClient. This has the side benefit of keeping your controllers from depending directly on HttpClient. Any class that depends on HttpClient becomes harder to test. It's also a maintenance issue because if you want to change the behavior you have to change it everywhere. Imagine if you decide one day that whatever you're getting from that HttpClient can be cached? You'd have to change it in lots of classes.
You can define an abstraction and implementation like this:
public interface IDoesSomething
{
string GetSetting(string key);
}
public class HttpClientDoesSomething : IDoesSomething, IDisposable
{
private readonly HttpClient _client;
private readonly string _apiUrl;
public HttpClientDoesSomething(string apiUrl)
{
_client = new HttpClient();
_apiUrl = apiUrl;
}
public string GetSetting(string key)
{
// use the client to retrieve the setting
}
public void Dispose()
{
_client?.Dispose();
}
}
Now the problem is moved out of your controller because you inject the interface:
public class MyController : Controller
{
private readonly IDoesSomething _doesSomething;
public MyController(IDoesSomething doesSomething)
{
_doesSomething = doesSomething;
}
public ActionResult Index()
{
var setting = _doesSomething.GetSetting("whatever");
// whatever else this does.
return View();
}
}
Now in your startup configuration you can register HttpClientDoesSomething as a singleton:
services.AddSingleton<IDoesSomething>(new HttpClientDoesSomething("url from settings"));
Your implementation is disposable, so if you do need to create and dispose it you will also dispose the HttpClient. But it won't be an issue because your application will keep reusing the same one.

Add test data in integration tests

I'm writing integration tests for my .NET Core app using xUnit. I have various things I want to test, but for brevity I'll use this example. I need to test these things ("document" is just a string, I'm not dealing with files):
Register new employee
Log in as employee
Add a new document for the employee
Add a comment to the document
Tests are of course supposed to run independently of each other, but I cannot add a comment to a nonexistent document. I need the document to exist to test adding a comment, but the employee must also exist to add the document.
I have test classes written for all entities I want to use, but I don't know how to actually get them in the database for a given test. How can I "seed" the DB for a given test?
TestStartup.cs:
public class TestFixture : IDisposable
{
readonly TestServer _server;
public HttpClient _client { get; }
public TestFixture()
{
var builder = new WebHostBuilder()
.UseContentRoot(#"..\..\..\..\..\src\MyProj.Web")
.UseEnvironment("IntegrationTestDevelopment")
.UseStartup<Web.Startup>();
_server = new TestServer(builder);
_client = _server.CreateClient();
_client.BaseAddress = new Uri("http://localhost:2856");
}
public void Dispose()
{
_client.Dispose();
_server.Dispose();
}
}
Relevant part of Web.Startup:
services.AddEntityFrameworkInMemoryDatabase()
.AddDbContext<MyContext>(options => options.UseInMemoryDatabase());
Example test class:
public class EmployeeTests : IClassFixture<TestFixture>
{
public HttpClient _client;
public EmployeeTests(TextFixture fixture)
{
_client = fixture._client;
}
[Theory]
[MemberData("ValidRegistrations")]
public async Task Register_new_employee_succeeds(EmployeeModel model)
{
var request = Helpers.CreateRequestMessage("POST", "/api/employees", model);
var response = await _client.SendAsync(request);
Assert.Equal(HttpStatusCode.Created, response.StatusCode);
}
}
I may be missing something here (knowledge of, but never used, .Net Core), but as you say this is an integration test rather than a pure unit test, so would an alternative approach be:
Have the integration test merely test the call / connectivity to the service (i.e to test that 'employees' can be called I think in your scenario - regardless of database content)
Create a new test (ideally a unit test, but let's face it integration tests definately have a place in this world) to test the functionality of the 'employees' method / function
If the above isn't an option for you, how about considering seeding the db in the test class (one of the method or class set up methods - I forget their names in xUnit) by calling simple database interaction code, doesn't have to be anything exotic or fancy if it's just a seed followed by a subsequent end-of-test tear down. A basic db connection and query execution would presumably suffice.
Appreciate this doesn't directly answer your question, more of an outsiders viewpoint.

Unit Testing a controller that uses windows authentication

-------Please see updates below as I now have this set up for dependency injection and the use of the MOQ mocking framework. I'd still like to split up my repository so it doesn't directly depend on pulling the windowsUser within the same function.
I have a Web API in an intranet site that populates a dropdown. The query behind the dropdown takes the windows username as a parameter to return the list.
I realize I don't have all of this set up correctly because I'm not able to unit test it. I need to know how this "should" be set up to allow unit testing and then what the unit tests should look like.
Additional info: this is an ASP.NET MVC 5 application.
INTERFACE
public interface ITestRepository
{
HttpResponseMessage DropDownList();
}
REPOSITORY
public class ExampleRepository : IExampleRepository
{
//Accessing the data through Entity Framework
private MyDatabaseEntities db = new MyDatabaseEntities();
public HttpResponseMessage DropDownList()
{
//Get the current windows user
string windowsUser = HttpContext.Current.User.Identity.Name;
//Pass the parameter to a procedure running a select query
var sourceQuery = (from p in db.spDropDownList(windowsUser)
select p).ToList();
string result = JsonConvert.SerializeObject(sourceQuery);
var response = new HttpResponseMessage();
response.Content = new StringContent(result, System.Text.Encoding.Unicode, "application/json");
return response;
}
}
CONTROLLER
public class ExampleController : ApiController
{
private IExampleRepository _exampleRepository;
public ExampleController()
{
_exampleRepository = new ExampleRepository();
}
[HttpGet]
public HttpResponseMessage DropDownList()
{
try
{
return _exampleRepository.DropDownList();
}
catch
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
}
}
}
UPDATE 1
I have updated my Controller based on BartoszKP's suggestion to show dependency injection.
UPDATED CONTROLLER
public class ExampleController : ApiController
{
private IExampleRepository _exampleRepository;
//Dependency Injection
public ExampleController(IExampleRepository exampleRepository)
{
_exampleRepository = exampleRepository;
}
[HttpGet]
public HttpResponseMessage DropDownList()
{
try
{
return _exampleRepository.DropDownList();
}
catch
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
}
}
}
UPDATE 2
I have decided to use MOQ as a mocking framework for unit testing. I'm able to test something simple, like the following. This would test a simple method that doesn't take any parameters and doesn't include the windowsUser part.
[TestMethod]
public void ExampleOfAnotherTest()
{
//Arrange
var mockRepository = new Mock<IExampleRepository>();
mockRepository
.Setup(x => x.DropDownList())
.Returns(new HttpResponseMessage(HttpStatusCode.OK));
ExampleController controller = new ExampleController(mockRepository.Object);
controller.Request = new HttpRequestMessage();
controller.Configuration = new HttpConfiguration();
//Act
var response = controller.DropDownList();
//Assert
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
I need help testing the DropDownList method (one that does include code to get the windowsUser). I need advice on how to break this method apart. I know both parts shouldn't been in the same method. I don't know how to arrange splitting out the windowsUser variable. I realize this really should be brought in as a parameter, but I can't figure out how.
You usually do not unit-test repositories (integration tests verify if they really persist the data in the database correctly) - see for example this article on MSDN:
Typically, it is difficult to unit test the repositories themselves, so it is often better to write integration tests for them.
So, let's focus on testing only the controller.
Change the controller to take IExampleRepository in its constructor as a parameter:
private IExampleRepository _exampleRepository;
public ExampleController(IExampleRepository exampleRepository)
{
_exampleRepository = exampleRepository;
}
Then, in your unit tests, use one of mocking frameworks (such as RhinoMock for example) to create a stub for the sole purpose of testing the controller.
[TestFixture]
public class ExampleTestFixture
{
private IExampleRepository CreateRepositoryStub(fake data)
{
var exampleRepositoryStub = ...; // create the stub with a mocking framework
// make the stub return given fake data
return exampleRepositoryStub;
}
[Test]
public void GivenX_WhenDropDownListIsRequested_ReturnsY()
{
// Arrange
var exampleRepositoryStub = CreateRepositoryStub(X);
var exampleController = new ExampleController(exampleRepositoryStub);
// Act
var result = exampleController.DropDownList();
// Assert
Assert.That(result, Is.Equal(Y));
}
}
This is just a quick&dirty example - CreateRepositoryStub method should be of course extracted to some test utility class. Perhaps it should return a fluent interface to make the test's Arrange section more readable on what is given. Something more like:
// Arrange
var exampleController
= GivenAController()
.WithFakeData(X);
(with better names that reflect your business logic of course).
In case of ASP.NET MVC, the framework needs to know how to construct the controller. Fortunately, ASP.NET supports the Dependency Injection paradigm and a parameterless constructor is not required when using MVC unity.
Also, note the comment by Richard Szalay:
You shouldn't use HttpContext.Current in WebApi - you can use base.User which comes from HttpRequestBase.User and is mockable. If you really want to continue using HttpContext.Current, take a look at Mock HttpContext.Current in Test Init Method
One trick that I find very useful when trying to make old code testable when said code is accessing some global static or other messy stuff that I can't easily just parameterize is to wrap access to the resource in a virtual method call. Then you can subclass your system under test and use that in the unit test instead.
Example, using a hard dependency in the System.Random class
public class Untestable
{
public int CalculateSomethingRandom()
{
return new Random().Next() + new Random().Next();
}
}
Now we replace var rng = new Random();
public class Untestable
{
public int CalculateSomethingRandom()
{
return GetRandomNumber() + GetRandomNumber();
}
protected virtual int GetRandomNumber()
{
return new Random().Next();
}
}
Now we can create a testable version of the class:
public class Testable : Untestable
{
protected override int GetRandomNumber()
{
// You can return whatever you want for your test here,
// it depends on what type of behaviour you are faking.
// You can easily inject values here via a constructor or
// some public field in the subclass. You can also add
// counters for times method was called, save the args etc.
return 4;
}
}
The drawback with this method is that you can't use (most) isolation frameworks to implement protected methods (easily), and for good reason, since protected methods are sort of internal and shouldn't be all that important to your unit tests. It's still a really handy way of getting things covered with tests so you can refactor them, instead of having to spend 10 hours without tests, trying to do major architectual changes to your code before you get to "safety".
Just another tool to keep in mind, I find it comes in handy from time to time!
EDIT: More concretely, in your case you might want to create a protected virtual string GetLoggedInUserName(). This will technically speaking keep the actual call to HttpContext.Current.User.Identity.Name untested, but you will have isolated it to the simplest smallest possible method, so you can test that the code is calling the correct method the right amount of times with the correct args, and then you simply have to know that HttpContext.Current.User.Identity.Name contains what you want. This can later be refactored into some sort of user manager or logged in user provider, you'll see what suits best as you go along.

Categories