{"id":17822081,"url":"https://github.com/thygesteffensen/powerautomatemockup","last_synced_at":"2025-09-10T23:33:19.445Z","repository":{"id":40799723,"uuid":"315675439","full_name":"thygesteffensen/PowerAutomateMockUp","owner":"thygesteffensen","description":"Skeleton to run Power Automate Flows from their JSON descriptions.","archived":false,"fork":false,"pushed_at":"2024-07-29T16:02:23.000Z","size":293,"stargazers_count":8,"open_issues_count":5,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-16T20:04:50.945Z","etag":null,"topics":["mock","power-automate"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thygesteffensen.png","metadata":{"files":{"readme":"ReadMe.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-11-24T15:33:34.000Z","updated_at":"2024-10-30T14:52:34.000Z","dependencies_parsed_at":"2024-10-27T18:15:59.764Z","dependency_job_id":"3810fc6c-1179-4519-9f18-b219de1c72fb","html_url":"https://github.com/thygesteffensen/PowerAutomateMockUp","commit_stats":null,"previous_names":[],"tags_count":42,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thygesteffensen%2FPowerAutomateMockUp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thygesteffensen%2FPowerAutomateMockUp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thygesteffensen%2FPowerAutomateMockUp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thygesteffensen%2FPowerAutomateMockUp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thygesteffensen","download_url":"https://codeload.github.com/thygesteffensen/PowerAutomateMockUp/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244222480,"owners_count":20418526,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["mock","power-automate"],"created_at":"2024-10-27T17:35:00.281Z","updated_at":"2025-03-18T12:31:36.083Z","avatar_url":"https://github.com/thygesteffensen.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003ePower Automate Mock-Up\u003c/h1\u003e\n\u003ch3 align=\"center\"\u003eSkeleton to run Power Automate flows from JSON flow definition.\u003c/h3\u003e\n\u003cp align=\"center\"\u003e\n    \u003cimg alt=\"Build status\" src=\"https://img.shields.io/github/workflow/status/thygesteffensen/PowerAutomateMockUp/Release/main?label=main\"\u003e\n        \u003cimg alt=\"Build status\" src=\"https://img.shields.io/github/workflow/status/thygesteffensen/PowerAutomateMockUp/Build/dev?label=dev\"\u003e\n    \u003ca href=\"https://www.nuget.org/packages/PowerAutomateMockUp/\"\u003e\n        \u003cimg alt=\"Nuget downloads\" src=\"https://img.shields.io/nuget/dt/PowerAutomateMockUp\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://www.nuget.org/packages/PowerAutomateMockUp/\"\u003e\n        \u003cimg alt=\"Nuget version\" src=\"https://img.shields.io/nuget/v/PowerAutomateMockUp\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://www.nuget.org/packages/PowerAutomateMockUp/\"\u003e\n        \u003cimg alt=\"Nuget prerelease version\" src=\"https://img.shields.io/nuget/vpre/PowerAutomateMockUp\"\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\u003c!-- \u003cp align=\"center\"\u003e\n    \u003ca href=\"https://thygesteffensen.github.io/PowerAutomateMockUp/Index\"\u003eHome\u003c/a\u003e\n    |\n    \u003ca href=\"https://thygesteffensen.github.io/PowerAutomateMockUp/GettingStarted\"\u003eGetting Started\u003c/a\u003e\n    |\n    \u003ca href=\"https://thygesteffensen.github.io/PowerAutomateMockUp/Technical\"\u003eTechnical\u003c/a\u003e\n\u003c/p\u003e --\u003e\n\n~~Currently there is not a way to unit test Power Automate flows. You have the ability to manually run a flow with static results, but this isn't the same as a unit test. I have during my work implemented business critical functionality in Power Automate using Common Data Service (current environment) connector.~~\n\nThere is a way to unit test Power Automate flows!\n\n## Installation\n\nA the [NuGet package](https://www.nuget.org/packages/PowerAutomateMockUp/) to your project.\n\n## How to use\n\n### Introduction\nThis is a skeleton and itself will not test anything OOB. Instead this is meant as the core, used to implement different connectors. As you probably know, Power Automate uses Connectors to interact with others services, such as Common Data Service. I have implemented [Common Data Service (current environment)](https://github.com/thygesteffensen/PAMU_CDS), go take a look to see how it can be used.\n\n### Getting Started\n\n```c#\nvar path = \"\u003cpath to flow definition\u003e\";\n            \n// from Microsoft.Extensions.DependencyInjection\nvar services = new ServiceCollection();\n\n// Required to set up required dependencies\nservices.AddFlowRunner(); \n\nvar sp = services.BuildServiceProvider();\n\nvar flowRunner = sp.GetRequiredService\u003cFlowRunner\u003e();\n\nflowRunner.InitializeFlowRunner(path);\n\nvar flowResult = await flowRunner.Trigger();\n\n// Your flow have now ran\n```\n\n### Configuration\nThis is optional and the settings class has the default values mentioned below.\n\nThe settings object is configured this way:\n```cs\nservices.Configure\u003cFlowSettings\u003e(x =\u003e { }); // Optional way to add settings\n```\nThe possible values to set is:\n\n * `x.FailOnUnknownAction` (default: `true`): If an action cannot be found and exception is thrown. This can be avoid and the action is ignored and the status is assumed to be `Succeeded`.\n * `x.IgnoreActions` (default: `empty`): List of action names which are ignored during execution, the action is not executed and the status is assumed to be `Succeeded`.\n* `x.LogActionsStates` (default: `true`): Logs JSON, parsed input and generated output for every action executed.\n\n### Asserting action input and output\n\nThe FlowReport from triggering the flow can be used to assert the input and output of an action.\n\nThis can be used to verify that the expected parameters to an action is present and you can assert the input is as expected.\n\n```c#\nvar greetingCardItems = flowReport.ActionStates[\"Create_a_new_row_-_Create_greeting_note\"]\n                .ActionInput?[\"parameters\"]?[\"item\"];\nAssert.IsNotNull(greetingCardItems);\nAssert.AreEqual(expectedNoteSubject, greetingCardItems[\"subject\"]);\nAssert.AreEqual(expectedNoteText, greetingCardItems[\"notetext\"]);\n```\n\n### Adding actions\nActions can be added in three ways\n\n1. Using specific action name\n2. Using Connection ApiId and supported OperationIds\n3. Using Action type (**Not recommended**)\n\n#### 1. Using specific action name\n```c#\nservices.AddFlowActionByName\u003cGetMsnWeather\u003e(\"Get_forecast_for_today_(Metric)\");\n```\n\nWhen the action named *Get_forecast_for_today_(Metric)* is reached and about to be executed, the class with type GetMsnWeather is retrieved from the ServiceProvider and used to execute the action.\n\n#### 2. Using Connection ApiId and supported OperationIds\n```c#\n// For OpenApiConnection connectors only\nservices.AddFlowActionByApiIdAndOperationsName\u003cNotification\u003e(\n    \"/providers/Microsoft.PowerApps/apis/shared_flowpush\", \n    new []{ \"SendEmailNotification\", \"SendNotification\" });\n```\n\nWhen an action from the **Notification** connector with one of the supported types is reached in the flow, a action executor instance of type `Notification` is created and used to execute the action.\n\n#### 3. Using Action type (**Not recommended**)\n```c#\nservices.AddFlowActionByFlowType\u003cIfActionExecutor\u003e(\"If\");\n```\nWhen the generic action type **If** i reached, an action executor instance of type `IfActionExecutor` is created and used to execute the action.\n\nThis is not recommended due to the fact that every OpenApiConnection connector will have the type **OpenApiConnection**. This means that both Common Data Service (current environment) and many others, will use the same action executors, which is not the correct way to do it.\n\nThis way of resolving an action executor is only used to resolve actions, where only one Action uses that type. This is **If**, **DoUntil** etc.\n\n### Creating action executors.\nCurrently there are two classes to extend, one is **DefaultBaseActionExecutor** and the other is **OpenApiConnectionBaseActionExecutor**.\n\n#### DefaultBaseActionExecutor\n```c#\nprivate class ActionExecutor : DefaultBaseActionExecutor\n{\n    private readonly IExpressionEngine _expression;\n\n    // Using dependency injection to get dependencies\n    public TriggerActionExecutor(IExpressionEngine expression)\n    {\n        _expression = expression ?? throw new ArgumentNullException(nameof(expression));\n    }\n\n    public override Task\u003cActionResult\u003e Execute()\n    {\n        var result = new ActionResult();\n\n        try\n        {\n            // Some dangerous operation\n            // ...\n\n            result.ActionOutput = new ValueContainer(new Dictionary\u003cstring, ValueContainer\u003e()\n            {\n                {\"Key\", new ValueContainer(\"Value\")}\n            });\n            // Corresponds to: outputs('\u003caction\u003e').Key || outputs('\u003caction\u003e')['Key']\n\n            result.ActionStatus = ActionStatus.Succeeded;\n        } \n        catch(EvenMoreDangerousException exp)\n        {\n            // PAMU handles the exceptions...\n            result.ActionStatus = ActionStatus.Failed;\n            result.ActionExecutorException = exp;\n        }\n\n        return Task.FromResult(result);\n    }\n}\n```\n\nThe execute method is called when the action is run.\n\n#### OpenApiConnectionBaseActionExecutor\n```c#\nprivate class ActionExecutor : OpenApiConnectionActionExecutorBase\n{\n    // To easier register the Action Executor\n    public const string FlowActionName = \"Update_Account_-_Invalid_Id\";\n\n    public override Task\u003cActionResult\u003e Execute()\n    {\n        // ... Execute action functionality\n \n        var parameters = Parameters;\n\n        var entityName = parameters[\"string\"].GetValue\u003cstring\u003e();\n\n        // ...\n    }\n}\n```\n\nWhen using OpenApiConnectionActionExecutorBase, some extra values form the Json definition is parsed and ready to use. These are currently values in the parameter object and host object.\n\n### Dependencies\nPower Automate MockUp heavily depends on dependencies and its a great way to easily get different classes, in your own class.\nBelow is a list of dependencies you might want to use, when executing flows.\n\n#### ExpressionParser\n```c#\nValueContainer value = expressionParser.Parse(\"\u003cexpression\u003e\");\n```\nAn expression is known from Power Automate such as `\"@toLower('Jonh Doe')\"` or `\"@outputs('Get_contacts')[2]\"`. \nEvery expression from Power Automate is supported in PowerAutomateMockUp. \n\nThe response is returned wrap in the ValueContainer.\n\n#### IState\nIState, and its implementation, is how the state of the execution of a Power Automate flow is handled. It contains the trigger values and can give the value of previous executed actions, by either using one of the interfaces below (for simplicity) or IState itself.\n\n## Tests\n\nTests are located in the **Tests** project and they are written using Nunit as test framework.\n\n\n## Contribute\n\nFeel free to create an issue with a suggestion, create a pull request or participate in any way you like :rocket:\n\n\u003c!--## Credits--\u003e\n\n## Code style\nThe code is written using [Riders](https://www.jetbrains.com/help/rider/Settings_Code_Style_CSHARP.html) default C# code style.\n\nCommits are written in [conventional commit](https://www.conventionalcommits.org/en/v1.0.0/) style, the commit messages are used to determine the version and when to release a new version. The pipeline is hosted on Github and [Semantic Release](https://github.com/semantic-release/semantic-release) is used.\n\n\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthygesteffensen%2Fpowerautomatemockup","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthygesteffensen%2Fpowerautomatemockup","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthygesteffensen%2Fpowerautomatemockup/lists"}