Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HttpTriggerTester Expression Tree Limitations #69

Open
m-flak opened this issue Feb 27, 2024 · 3 comments
Open

HttpTriggerTester Expression Tree Limitations #69

m-flak opened this issue Feb 27, 2024 · 3 comments
Labels
question Further information is requested

Comments

@m-flak
Copy link

m-flak commented Feb 27, 2024

HttpTriggerTester Expression Tree Limitations

Background

In my tests project, I have created an extension method using reflection to automatically get the route of my HTTP-Triggered-Functions with that method.
The goal here was to eliminate requirements for test code updates after changes to a Function's route in the main code. It almost works.

I've since written my code around this limitation, but it requires me to spin up & run an entire HostTesterBase implementor twice. 😰

Issue

HttpTriggerTester's Run/RunAsync methods, unlike TypeTester's, uses expression trees. SRC

public async Task<ActionResultAssertor> RunAsync(Expression<Func<TFunction, Task<IActionResult>>> expression)

This means that I am unable to write something like this without experiencing compilation errors:

        var sut = await test
            .HttpTrigger<QueueTaskCreateFunction>()
            .RunAsync(f => f.Run(test.CreateHttpRequest(HttpMethod.Post, f.GetFunctionRoute(), contentType: MediaTypeNames.Text.Plain), context.Object));

A statement like this would work with TypeTester, but I can't use TypeTester to test my HTTP-Triggered-Function.

Is there a technical reason for HttpTriggerTester's Run/RunAsync methods not having an overload for Action<T> or Func<T1,T2>??

Workaround

The following boilerplate accomplishes what I intended without any issues:

       var route = test
            .Type<QueueTaskCreateFunction>()
            .Run(f => f.GetFunctionRoute())
            .Result;

        var request = test
            .CreateHttpRequest(HttpMethod.Post, route, contentType: MediaTypeNames.Text.Plain);

        var sut = await test
            .HttpTrigger<QueueTaskCreateFunction>()
            .RunAsync(f => f.Run(request, context.Object));
@chullybun chullybun added the question Further information is requested label Feb 29, 2024
@chullybun
Copy link
Collaborator

Hi @m-flak,

The expression usage is by design as this provides an opportunity for UnitTestEx to verify/assert that the method being invoked as HTTP, i.e. has the HttpTriggerAttribute. And, that the corresponding HTTP method is expected.

A backlog item is for this to further inspect the Route and verify what is passed matches accordingly. Today, this is accepted as-is and technically anything can be passed in as the route content.

I am not sure that a test should infer a value in the code being tested and then be used in the actual test. The test should assert based on intent; otherwise, how do you know whether there is an issue, e.g. maybe a misspelling in the route?

How often do the routes change that this is an issue?

Thanks...

@m-flak
Copy link
Author

m-flak commented Mar 3, 2024

I am not sure that a test should infer a value in the code being tested and then be used in the actual test. The test should assert based on intent; otherwise, how do you know whether there is an issue, e.g. maybe a misspelling in the route?

How often do the routes change that this is an issue?

Okay, all I wanted to do was get the route value automatically from the attribute and use it with CreateHttpRequest. There's no way to do that currently, so I attempted to implement that on my end.

A backlog item is for this to further inspect the Route [...]

This is the gap I tried to fill that led me to this issue. I wanted the ability to use the metadata within HttpTriggerAttribute to automatically create test http requests with the correct route.

@m-flak
Copy link
Author

m-flak commented Mar 6, 2024

I rethought my approach and made a simple utility method.
Still, it would be cool if UnitTestEx could do this for me.

    public static string GetHttpRoute(Type functionClass)
    {
        string? route = null;

        var runMethod = functionClass.GetMethod("Run");
        var runParams = runMethod?.GetParameters();
        if (runMethod is null
            || runParams is null
            || runParams.Length == 0)
        {
            return string.Empty;
        }

        var attribute = runParams[0]
            .GetCustomAttributes(true)
            .FirstOrDefault(
                a => typeof(HttpTriggerAttribute).IsInstanceOfType(a),
                null)
            as HttpTriggerAttribute;

        route = attribute?.Route;
        route ??= string.Empty;
        return route;
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants