From d3162af0da99be7a5006cd7b8d4c24f247790d28 Mon Sep 17 00:00:00 2001 From: Sakari Malkki Date: Mon, 13 Oct 2025 09:44:16 +0300 Subject: [PATCH 1/4] Readme for AzureNetIsolatedFunctionMockTools project --- AzureNetIsolatedFunctionMockTools/readme.md | 111 ++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 AzureNetIsolatedFunctionMockTools/readme.md diff --git a/AzureNetIsolatedFunctionMockTools/readme.md b/AzureNetIsolatedFunctionMockTools/readme.md new file mode 100644 index 0000000..ebe1a05 --- /dev/null +++ b/AzureNetIsolatedFunctionMockTools/readme.md @@ -0,0 +1,111 @@ +# Azure .NET Isolated Function Mock Tools + +A library providing mock implementations for Azure .NET Isolated Functions, designed to simplify unit testing by reducing the bootstrapping required to mock Azure Functions infrastructure. + +## Overview + +This library provides ready-to-use mock implementations of abstract classes and interfaces commonly used in Azure .NET Isolated Functions. Instead of manually creating test doubles or writing complex setup code, you can use these mock implementations to focus on testing your business logic. +The mock implementations are designed to be as un-opinionated as possible, allowing you to override only the properties and methods you need for your tests. All constructor parameters are optional and have sensible defaults, making it easy to create instances with minimal configuration. + +## Installation + +```bash +dotnet add package AzureNetIsolatedFunctionMockTools +``` + +## Available Mock Implementations + +### MockFunctionContext + +Mock implementation of `FunctionContext` for testing purposes. + +**Constructor Parameters** (all optional): +- `invocationId` - Unique identifier for the function invocation (default: empty string) +- `functionId` - Unique identifier for the function (default: empty string) +- `traceContext` - TraceContext object (default: new MockTraceContext) +- `bindingContext` - BindingContext object (default: new MockBindingContext) +- `retryContext` - RetryContext object (default: new MockRetryContext) +- `instanceServices` - IServiceProvider object (default: new MockServiceProvider) +- `functionDefinition` - FunctionDefinition object (default: new MockFunctionDefinition) +- `items` - IDictionary for storing items (default: new Dictionary) +- `features` - IInvocationFeatures object (default: new MockInvocationFeatures) + +**Usage:** +```csharp +var context = new MockFunctionContext( + invocationId: "test-invocation-123", + functionId: "MyFunction" +); +``` + +### MockHttpRequestData + +Mock implementation of `HttpRequestData` for testing HTTP-triggered functions. + +**Constructor Parameters:** +- `functionContext` - **Required** FunctionContext object (recommended: MockFunctionContext) +- `body` - Request body stream (default: new MemoryStream) +- `headers` - HTTP headers collection (default: empty collection) +- `cookies` - HTTP cookies collection (default: empty collection) +- `url` - Request URL (default: https://example.com) +- `identities` - ClaimsIdentity collection (default: empty collection) +- `method` - HTTP method string (default: empty string) +- `response` - HttpResponseData object (default: new MockHttpResponseData) + +**Usage:** +```csharp +var context = new MockFunctionContext(); +var request = new MockHttpRequestData( + functionContext: context, + method: "POST", + url: new Uri("https://example.com/api/test"), + body: new MemoryStream(Encoding.UTF8.GetBytes("{\"test\": \"data\"}")) +); +``` + +### MockHttpResponseData + +Mock implementation of `HttpResponseData` for testing HTTP responses. + +**Constructor Parameters:** +- `functionContext` - **Required** FunctionContext object (recommended: MockFunctionContext) +- `statusCode` - HTTP status code (default: HttpStatusCode.OK) +- `headers` - HTTP headers collection (default: empty collection) +- `stream` - Response body stream (default: new MemoryStream) +- `cookies` - HTTP cookies collection (default: empty collection) + +**Usage:** +```csharp +var context = new MockFunctionContext(); +var response = new MockHttpResponseData( + functionContext: context, + statusCode: HttpStatusCode.Created +); + +// Write to response body +response.Body.Write(Encoding.UTF8.GetBytes("Response content")); +``` + +### MockServiceProvider + +Mock implementation of `IServiceProvider` for dependency injection testing. + +**Constructor Parameters:** +- `services` - Dictionary of services (default: empty dictionary) + +**Usage:** +```csharp +var services = new Dictionary +{ + { typeof(IMyService), new MyServiceMock() }, + { typeof(IConfiguration), configurationMock } +}; + +var serviceProvider = new MockServiceProvider(services); +var context = new MockFunctionContext(instanceServices: serviceProvider); +``` + +## Requirements + +- .NET 8.0 or later +- Microsoft.Azure.Functions.Worker package \ No newline at end of file From 38c5e23e919044de96881f68a9d5a1140c42b66e Mon Sep 17 00:00:00 2001 From: Sakari Malkki Date: Mon, 13 Oct 2025 09:53:13 +0300 Subject: [PATCH 2/4] Raise package version number --- .../AzureNetIsolatedFunctionMockTools.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AzureNetIsolatedFunctionMockTools/AzureNetIsolatedFunctionMockTools.csproj b/AzureNetIsolatedFunctionMockTools/AzureNetIsolatedFunctionMockTools.csproj index 151d0ed..50b851d 100644 --- a/AzureNetIsolatedFunctionMockTools/AzureNetIsolatedFunctionMockTools.csproj +++ b/AzureNetIsolatedFunctionMockTools/AzureNetIsolatedFunctionMockTools.csproj @@ -6,7 +6,7 @@ enable Azure .NET Isolated Function Mock Tools true - 1.0.0 + 1.1.0 StatisticsFinland StatisticsFinland Tools for mocking Azure .NET Isolated functions. From 7fbd3fa2b725f08555ff93f01d77f381113098e3 Mon Sep 17 00:00:00 2001 From: Sakari Malkki Date: Wed, 22 Oct 2025 09:28:49 +0300 Subject: [PATCH 3/4] Use explicit typing in code examples --- AzureNetIsolatedFunctionMockTools/readme.md | Bin 4236 -> 8634 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/AzureNetIsolatedFunctionMockTools/readme.md b/AzureNetIsolatedFunctionMockTools/readme.md index ebe1a051a0e4b1fb4f5ac3c2119b3b6fd3d6e6e0..cbee0ed52ac500f7aa560926973a37f98c0f4e91 100644 GIT binary patch literal 8634 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 Date: Wed, 22 Oct 2025 15:05:40 +0300 Subject: [PATCH 4/4] Fix readme md encoding --- AzureNetIsolatedFunctionMockTools/readme.md | Bin 8634 -> 4206 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/AzureNetIsolatedFunctionMockTools/readme.md b/AzureNetIsolatedFunctionMockTools/readme.md index cbee0ed52ac500f7aa560926973a37f98c0f4e91..4622dcd61828b01100d3e7c198ed93f70da42b54 100644 GIT binary patch literal 4206 zcmc&&OLN;c5We$QVC0LWvFJW788yv}<0Oryb>gw*$;AgjBq*UK2?hY|uo?gNehcs= zI&_n?GxfrqGLc(OI))`mKWWt*vsf6p%W;!c%#glJDpuywGAoQF%iD@+ z5tkADZ})<2m9oZaUKI31i;tzmd(wA4_$V5`4U>7IIpoX8BD^pNj*i&-JE8BSxDSH$ zUYbEP!oVnxtA$mI(ugldrV_@twIRy?p17@d@hhjf|T=;GGan@nx+UFsiOkp zUBNYnlF+CJ+*1W1k*BUWdwwU7soXgO&l6sz78uOypa#w1Ha*rq~9)fdEf?%`RY`NJ7v9h_ajK?wjif;K%co4i3wqaNURN|MX z7nB`HPw%))Inw62w>sn&i5$5%jfFD>?0vF9;HY{?MFQ1kAh>b4Y*<#58f6j&;mqeS z)h6_!P3U~iriii4>1-J+*hc1eDspkJ;tgA{>s)>;k-8XdKqgXXe+^2TG`*x#Vbi(_ zOD3|yRt(po6f-j1h<~-&Cu|W>MV%mWa@{*Kwf$3o#+`aD?pZ)hzvUeUg)R%H*8jN01O9NCXyj6X;aP1i-7!7eCc#!y4A_3ruKV-8( zPxo}(>SI~&suh{{@0D~@1ta9SckQ?~_QIjXfXSYbP#6@ZQ}xcGL87mFv#S=1Z|cY6 zK0cP65%jMOSh}3giNlfEb6q$-H4)YIq8318W4|(hWg0O2b*Ns&OEw{5ExKomAAkCJ za_j^jX!eOb7Dxnjqk*6++*ZXE;i?W0V?B2C7d#tO{9I&8 zS63FZFmtMVj7c0UVtky5u9F|CFA{0$aLo%X%IzWk$i z=N^SK%aarFiBs1Okq&pQZ;CYBNcSw|GBYdJDAD>l7kiez=)E6`t@Hj^x6Vhpy^ac+ z6zJ^2X*z8AGl6TIC>~s$YCu{eYuWJ46UYY)@?5;XTGymP zJ(k2pcCF=f@`VDN@IsytDJL^;g#hPO`T(20&ap+I`GnW&H_yV~gnd~-YdZOS<0ySI zS+bi+jEvn(9wxKd2x+@?{S#|D$;Y%sYW(Z}o2*!FY-!H0Ubd+7k5CwFZ{6sm+FrH7 z;rrkJBfP^)UoSD z^ojkmo=T>-=yc1#^L16=1H+G?7qss-ay@kVZW_1C$5<;HHQU0+k#y6d@2j6=-1$4OXTLMbRd3Sc3{6p}7($c@R>2|BDy=l+hsm8|| zeS_KCbkZ7o!|id|q^&X9mEKKiOn&;l$BzID|4YHM@CW)HrC$p)o}J4`E29z{y6^C? Qx!r^xl)l9#T7L)q4d7bOsQ>@~ literal 8634 zcmeI2T~Av_5Qg`Wl9!&%tZ=T3MTeh58%>V`5*!g1(_ znMOu>@?5h{!%%;ZHFBya+j<{{BYo=W6SK?kdH5{8p6KbZW)38|uh*sip6Y!P{?OBr zUZ>$u&raqt#`-+fnti?RYjhTMo32tb4>a>k?>Du#3=gz=B-y33A4-GmYbLoxd}jK` z(uB3_b}TE3=z%is+iV`haXdECtl@kwv+!2W3hfzh%p`LbcR$s>e7~&vU8}|#N%dEL zNcWff96uoGAj&oQSTK`BSvr<|%VUJ6iYT|QonD4b z>6z&Fxt_Dn%I1&7ZX$~d#d9$C2{sq99E6oo9=iJ48(3qH;ON8`Y6trGOX*|O-VA4EqNV{$n4C&<2FiK*FNOkMoI@3z74dOhW zvm3+9M7cAT1j8Cgx*ir_16C}`#6cVd6_$gWveNR$xS(Prn={oow!t-c9lu$uCPI9D zPwbU!hlM~FQNa%Lgkj3F*w|_s=Mg!Jry~@OVkWV|QjZj#GDJFGRo-+zm+~E01y2@l z!`+#@OFQ?hNQRm1`hjPygDfisTu`!M>UV$_#$W?tKJmfer(fm-|^}w^~{;Z zs9Bbg169=Shx@_gq?O3fyCaX}=w*Hp)-__t&oR6a9>x7_YSy0ibt>-?i3$l_#TQ$d zv8(ZMtfhuY@)$o+zsXkktJ;&X)PeR=?_t`sU?bA1OnD?(^d;mRb;tH=9@qN-bX5{f^FGj054uS^ZH?6aZ-KL-XyO0dW;-UqhZ^mNIbo7lBX zUTPZ*i@z=x`0AGh<2SKsk*w4naF+7zYw5S1&e+#8q8sWXJDTPFw&lb1W4T;nYGsc7 zXR?w^v@Y5h-!08Ku5Ip3yt#_p)MjcHd0oy~r5m2fX0Yzv1~_dEi*w=)fK zf&q;iU}x9R&MKZeZCX=H4`e4iXnlTaZ&x19dGdKSyKe7tOFWBLk!u!Gli>DU`O#|b zqxd(^t!fT6LSJ$w2IZ;i$m3P#0q>3QAhoXu-|1x@TaR)I{ml>F&V2y)!rS*^1c=LK z>%7hIW%w$5t$7b>GIKmv^0tMD-12*I?MC=EzBhY?HCf`jej!_C@&vihS&*ta6Yhv_ zKdw)Gv}%Q4E|tu4jOG>oh1kCrcH;RCzk-&PebJ=r%1X5s=T4ZW3yahnvtCHITgE=ZlOifZUGqgZh`zg(O=6e zC~O3OT#C!7LAEN@OD998YbOHN?lCNsXX&`*5dD@_aAUQ0E7A@JFXgLJD;S@|a|8Qr zT2UXrD=T2bMR^BBCOL4?Q{lGB9$NKfo6-v?_kE746!=yT-``(fj zoCByN@Pv1w)-AUA^7au(PoPczr=yNQuSsnPOk)olc?C;702)hqi1P?{50%!6&(d*c zv}PXSp?Y`ibugjHl&t;r0OhKuUBcrt?upU z{_nMzJ+Ew*HOz6Z@5XqqYvyWs@la^zPUfMIVtjWmY-IJ*`7zbAt=Vn-wys-e&SfyF zp|qoJqNghRjn11tYSk<}6(^cD?0g@7)o<6%XY}wsl5NycK7Y__LqFieqqcf&Zo?gt zZd;w(*IG-4UM<0V{jX&i78_4t}h{bKV?Vo->U_w->X`fSJAtw2W>e4 zM=nS9)>Y35vyN)KiGS&oitr{@3ugFzdUH?C{;bMJJK}QdojZxF>(_uLwyXL`l{D^O z{2!^t|qWX6U zZMU)cS&WXZ?N+JnX5z7G^JTQR?JmT=H_PR<^K9C8Qv36pmz(x^*SET9Z+hEccD#=` zC=?lf-s?q&Pu$q^x_W=^2I^>{FRD7N=D4G3T@|HJ$5QX=+H_cy+rCjTU6uuH