116

I have a few C# Azure Functions that run on a schedule using timer triggers. I've set them up like so, where %TimerSchedule% refers to a cron expression in the app settings:

public static void Run([TimerTrigger("%TimerSchedule%")]TimerInfo myTimer, TraceWriter log)

During development, I often want to run the functions locally using Azure Functions Tools for Visual Studio + Azure Functions Core Tools. But when I hit F5 to debug the function locally it (usually) doesn't run immediately. Instead, it starts waiting for the next occurrence as per the timer schedule. So for example, if my cron expression says to run daily at 8PM, I'd have to wait until 8PM for the function to actually run on my machine.

So my question is: What is the simplest and best way to make a function run once locally?

Things I have tried or considered:

  1. Use a more frequent timer schedule just for local development
    • This is OK but not perfect – you still have to wait a little bit unless it's very frequent, and if it's very frequent then the function might run multiple times. This is what I'm doing now.
  2. Write a console app or unit test that directly calls the function's Run() method
    • This isn't 100% straightforward because you have to provide TimerInfo and TraceWriter arguments to Run() – and I've found surprisingly little documentation for that.

Microsoft's Strategies for testing your code in Azure Functions page is not very helpful on this topic – it only mentions timer triggers as a way to test other trigger types.

In a perfect world, I'd hit F5 and the function would immediately run once – just like developing a "normal" .NET app.

Reilly Wood
  • 1,753
  • 2
  • 12
  • 17

9 Answers9

131

I had the same question, and used the DEBUG-flag to have the RunOnStartup only while debugging:

        public static void Run(
            [TimerTrigger("* 0 7 * * 1-5"
#if DEBUG
            , RunOnStartup=true
#endif
            )]TimerInfo myTimer, TraceWriter log)
        {
BerDev
  • 1,457
  • 1
  • 8
  • 16
96

You could perhaps use the RunOnStartup flag as documented here. It doesn't quite meet your brief regarding it only running once, but it should at least execute it locally once the app has started.

/// Gets or sets a value indicating whether the function should be invoked
/// immediately on startup. After the initial startup run, the function will
/// be run on schedule thereafter.

Example using attribute binding:

[TimerTrigger("%TimerSchedule%", RunOnStartup = true)]TimerInfo myTimer

Rowland Shaw
  • 37,700
  • 14
  • 97
  • 166
Wah Yuen
  • 1,569
  • 1
  • 9
  • 13
  • 1
    Am I correct in thinking that this would also run the function once in the cloud whenever I deploy the function? [This comment](https://stackoverflow.com/a/44573176/854694) seems to indicate yes. If so, this is a big step in the right direction but it's a little unfortunate that it would have to be reverted before deploying if I don't want to change "production" behaviour. – Reilly Wood Oct 04 '17 at 15:12
  • 3
    @ripley_ yes, i believe you would be correct. An option could be that you bind the boolean into a variable in your config, similar to how you have bound your 'TimerSchedule'. From there, you could have your local development set to true and your production (or other) environments set to false, as you see fit. – Wah Yuen Oct 05 '17 at 00:14
  • 2
    How can I bind RunOnStartup to a boolean defined in my config. "%%" syntax does not work for non-string values. – user2297037 Apr 20 '18 at 10:49
  • From [Microsoft documentation](https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-timer?tabs=csharp) it seems to recommend against using the RunOnStartup parameter for most cases. ```If true, the function is invoked when the runtime starts. For example, the runtime starts when the function app wakes up after going idle due to inactivity. when the function app restarts due to function changes, and when the function app scales out. **So runOnStartup should rarely if ever be set to true, especially in production**``` – reddi.tech Dec 13 '19 at 00:20
  • @Nissan this is true, however, the OP stated this was for running locally and not in a production environment... – Wah Yuen Dec 13 '19 at 05:11
  • @WahYuen I want this `%TimerSchedule%` to come from compile time variable. Is it possible? – Niklaus Nov 05 '20 at 14:52
  • @Niklaus I'm confused, this already is a compile time variable? Did you mean 'runtime' variable instead? If so, you can go down the imperative binding route to perform this https://learn.microsoft.com/en-us/azure/azure-functions/functions-dotnet-class-library?tabs=v2%2Ccmd#binding-at-runtime – Wah Yuen Nov 12 '20 at 02:31
  • @WahYuen Sorry, my bad. I meant runtime variable. Anyhow, I found solution to my problem from your original answer. Thanks. – Niklaus Nov 12 '20 at 14:27
  • @ReillyWood You can use the #if DEBUG flags to only have this behaviour when debugging locally, not when releasing. – nivs1978 Nov 08 '22 at 11:41
90

From https://learn.microsoft.com/en-us/azure/azure-functions/functions-run-local?tabs=windows%2Ccsharp%2Cbash#non-http-triggered-functions

Non-HTTP triggered functions

For all kinds of functions other than HTTP triggers and webhooks, you can test your functions locally by calling an administration endpoint. Calling this endpoint with an HTTP POST request on the local server triggers the function. You can optionally pass test data to the execution in the body of the POST request. This functionality is similar to the Test tab in the Azure portal.

You call the following administrator endpoint to trigger non-HTTP functions:

http://localhost:{port}/admin/functions/{function_name}

To pass test data to the administrator endpoint of a function, you must supply the data in the body of a POST request message. The message body is required to have the following JSON format:

{
    "input": "<trigger_input>"
}
Lozenger
  • 450
  • 5
  • 5
reddi.tech
  • 2,183
  • 1
  • 16
  • 18
22

If you are using VS Code, use the Azure Functions extension:

  1. Hit F5 to enter debug mode, this starts the function app.
  2. Go to the Azure icon in the Activity bar.
  3. Under Local Project, find the function you want to run, right click, and select "Execute Function Now".

Check out this MS quickstart guide.

Renel Chesak
  • 577
  • 6
  • 16
12

Using postman should do the trick. Follow the below steps to Run or debug you Timer Trigger Locally.

1 . RUN your Project.

  1. Open Postman and past this url http://localhost:{port}/admin/functions/{function_name}

  2. Make sure to use a POST Method with Json body of { "input": "" }

  3. Press SEND.

You Should receive a response of 202.

maxspan
  • 13,326
  • 15
  • 75
  • 104
6

I had the same question. I fixed it with a Unittest. Indeed you need to stub out the TraceWriter and the TimerInfo.

Here some code how I did this.

TimerInfo:

public class ScheduleStub : TimerInfo
{
    public ScheduleStub(TimerSchedule schedule, ScheduleStatus status, bool isPastDue = false) : base(schedule, status, isPastDue)
    {
    }
}

And the TraceWriter:

 public class TraceWriterStub : TraceWriter
{
    protected TraceLevel _level;
    protected List<TraceEvent> _traces;

    public TraceWriterStub(TraceLevel level) : base(level)
    {
        _level = level;
        _traces = new List<TraceEvent>();
    }

    public override void Trace(TraceEvent traceEvent)
    {
        _traces.Add(traceEvent);
    }

    public List<TraceEvent> Traces => _traces;
}
StefanHa
  • 727
  • 8
  • 19
6

Start your function with this curl command

curl --request POST -H "Content-Type:application/json" --data '{"input":""}'  http://localhost:7071/admin/functions/{function_name}

The input data is required, without it the function won't be triggered.

Alexander van Trijffel
  • 2,916
  • 1
  • 29
  • 31
4

Another approach is to trigger manually the function from Postman:
Manually run a non HTTP-triggered function.

POST /admin/functions/<function name> HTTP/1.1
Host: localhost:<port>
Content-Type: application/json

{}

For me, it looks like that on postman for a timerTrigger function called Function1: enter image description here

Thomas
  • 24,234
  • 6
  • 81
  • 125
  • This is just to trigger it. How can you manually set the schedule using postman request? – Eliyah Feb 16 '23 at 15:31
  • Hi there. I feel this is a different question. Feel free to post a question and let us know the link to try to answer it – Thomas Feb 16 '23 at 18:43
1

Just add another function with HTTP trigger type within the same class, add your code, or call your Run method from that function and invoke it from your browser.

Be sure to comment/remove that function when deployed to prod, or you will have the ability to trigger the function via HTTP calls in prod.

double-beep
  • 5,031
  • 17
  • 33
  • 41