DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workkloads.

Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • How to Enhance the Performance of .NET Core Applications for Large Responses
  • Developing Minimal APIs Quickly With Open Source ASP.NET Core
  • Revolutionizing Content Management
  • Revolutionizing API Development: A Journey Through Clean Architecture With Adapter Pattern in ASP.NET Core

Trending

  • Unlocking AI Coding Assistants Part 3: Generating Diagrams, Open API Specs, And Test Data
  • Testing SingleStore's MCP Server
  • Integrating Security as Code: A Necessity for DevSecOps
  • Beyond ChatGPT, AI Reasoning 2.0: Engineering AI Models With Human-Like Reasoning
  1. DZone
  2. Testing, Deployment, and Maintenance
  3. Testing, Tools, and Frameworks
  4. Functional Testing For ASP.NET Core API

Functional Testing For ASP.NET Core API

Functional testing is the next level above the unit testing and gives us confidence that the code we wrote works as intended.

By 
Andrew Kulta user avatar
Andrew Kulta
·
Mar. 10, 22 · Tutorial
Likes (2)
Comment
Save
Tweet
Share
6.5K Views

Join the DZone community and get the full member experience.

Join For Free

What Is Functional Testing, Actually?

Functional testing is the process through which we determine if a piece of software is acting following pre-determined requirements. It uses black-box testing techniques, in which the tester does not know the internal system logic.

In the scope of API, we need to prepare possible requests that we expect to get and be sure that endpoints return a proper response for any payload.

Should We Even Spend Time on It?

Definitely! If you don't write integrational tests, then functional testing is a solution to ensure that your code has fewer bugs than it could have. When you can write integrational tests as well, it should be simple to reuse a shared codebase for functional and integrational tests. One difference will be in the proper environmental setup.

Let's Define a Simple API

Our controller contains only single action to create some entity for test purposes. Our business rules may require any entity, which doesn't matter for our test example. Besides, we should remember about authentication nowadays. Security compliance requires protecting API with any authentication. Let's assume that the current API requires an Authorization header with a Bearer token.

C#
 
[ApiController]
public class MyController : ControllerBase
{
    private readonly IService _service;
    
    public MyController(IService service)
    {
        _service = service;
    }
   
    [HttpPost("entities")]
    [ProducesResponseType(typeof(CreateResponse), (int)HttpStatusCode.OK)]
    [ProducesResponseType(typeof(ExceptionResponse), (int)HttpStatusCode.BadRequest)]
    public Task<CreateResponse> CreateAsync([FromBody] CreateRequest request)
    {
        return _service.CreateAsync(request);
    }
}


What Will the Functional Test Look Like for This Controller?

In our test project, I used NUnit as a test framework, but it's up to you to choose which better suits your needs. NUnit has all the required functionality to write good tests. Also, it's pretty popular on the market.

Let's add the following packages to the test project:

dotnet add package NUnitdotnet add package NUnit3TestAdapter


C#
 
[TestFixture]
public class ApiControllerTests
{
    private const string CreateUrl = "/entities";

    private readonly TokenBuilder _tokenBuilder = new();

    private TestServer? _server;
    private HttpClient? _client;

    [SetUp]
    public void Setup()
    {

        var settings = new Settings
        {
            SymmetricFuncTestKey = "your key for functional tests"
        };

        var builder = new WebHostBuilder()
            .UseStartup<Startup>()
            .ConfigureTestServices(services =>
            {
                // Mock your external services here
            })
            .ConfigureServices(services =>
            {
                // Change authentication to use bearer token which signed by symmetric key
                services.PostConfigure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
                {
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        IssuerSigningKey =
                            new SymmetricSecurityKey(Encoding.UTF8.GetBytes(settings.SymmetricFuncTestKey)),
                        ValidateIssuerSigningKey = true,
                        ValidateIssuer = true,
                        ValidateAudience = true,
                        ValidateLifetime = true,
                        ValidIssuer = TestConstants.Issuer,
                        ValidAudience = TestConstants.Audience,
                    };
                });
            });
        _server = new TestServer(builder);
        _client = _server.CreateClient();
    }

    [Test]
    public async Task CreateAsync_Should_ReturnNotAuthenticated_When_RequestWithoutAuthToken()
    {
        // Arrange
        var request = new CreateRequest
        {
            PropertyName = "propertyValue"
        };
        var json = JsonConvert.SerializeObject(request);
        using var data = new StringContent(json, Encoding.UTF8, "application/json");

        // Act
        var response = await _client?.PostAsync(CreateUrl, data)!;

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
    }

    [Test]
    public async Task CreateAsync_Should_Return400_When_RequestValidationFailed()
    {
        // Arrange
        var request = new CreateRequest();
        var json = JsonConvert.SerializeObject(request);
        using var data = new StringContent(json, Encoding.UTF8, "application/json");
        var token = await _tokenBuilder.BuildJWTToken();
        _client?.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);

        // Act
        var response = await _client?.PostAsync(CreateUrl, data)!;

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
    }

    [Test]
    public async Task CreateAsync_Should_ReturnCreateResponse()
    {
        // Arrange
        var request = new CreateRequest
        {
            PropertyName = "propertyValue"
        };
        var json = JsonConvert.SerializeObject(request);
        using var data = new StringContent(json, Encoding.UTF8, "application/json");
        var token = await _tokenBuilder.BuildJWTToken();
        _client?.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);

        var expected = new CreateResponse
        {
            PropertyName = "propertyValue"
        };

        // Act
        var response = await _client?.PostAsync(CreateUrl, data)!;

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);
        var responseBody = await response.Content.ReadAsStringAsync();
        var responseObject = JsonConvert.DeserializeObject<CreateResponse>(responseBody);
        responseObject.Should().BeEquivalentTo(expected);
    }
}


We should cover that API endpoint protected by authentication, and clients can't request it without a proper token. To do this, we need to prepare a test server with a proper authentication setup. To do it in Startup, we need to override JwtBearerOptions with our test authentication, which relies on auth token signed by SymmetricSecurityKey. To comply with security rules, we should avoid storing any secrets in code, and I don't suggest you violate this rule even for test projects.

When the authentication configuration is ready, we can generate the proper token to execute business logic in test cases. We can separate token generation into TokenBuilder and use it to get auth token.

Conclusion

I hope you feel that functional tests are pretty simple and allow you to verify your API each time something changes. We often include functional tests as a step in your build pipeline, and it gives you confidence that everything works well before deploying to the production environment.

Any questions or comments? Ping me on LinkedIn or leave a comment. And if you liked this post, please give it a clap and share it with all of your friends.

API Functional testing ASP.NET Testing ASP.NET Core

Published at DZone with permission of Andrew Kulta. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • How to Enhance the Performance of .NET Core Applications for Large Responses
  • Developing Minimal APIs Quickly With Open Source ASP.NET Core
  • Revolutionizing Content Management
  • Revolutionizing API Development: A Journey Through Clean Architecture With Adapter Pattern in ASP.NET Core

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!