1"use strict";(globalThis.webpackChunktunit_docs_site||=[]).push([[5985],{16422(e,n,t){t.r(n),t.d(n,{assets:()=>c,contentTitle:()=>r,default:()=>u,frontMatter:()=>a,metadata:()=>s,toc:()=>l});const s=JSON.parse('{"id":"reference/test-configuration","title":"Test Configuration","description":"TUnit supports having a testconfig.json file within your test project.","source":"@site/docs/reference/test-configuration.md","sourceDirName":"reference","slug":"/reference/test-configuration","permalink":"/docs/reference/test-configuration","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{},"sidebar":"docs","previous":{"title":"Environment Variables","permalink":"/docs/reference/environment-variables"},"next":{"title":"Tips & Pitfalls","permalink":"/docs/guides/best-practices"}}');var i=t(74848),o=t(28453);const a={},r="Test Configuration",c={},l=[{value:"Example",id:"example",level:2},{value:"Missing Keys and Files",id:"missing-keys-and-files",level:2},{value:"Nested Key Syntax",id:"nested-key-syntax",level:2},{value:"Typed Configuration",id:"typed-configuration",level:2}];function d(e){const n={admonition:"admonition",code:"code",h1:"h1",h2:"h2",header:"header",p:"p",pre:"pre",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.header,{children:(0,i.jsx)(n.h1,{id:"test-configuration",children:"Test Configuration"})}),"\n",(0,i.jsxs)(n.p,{children:["TUnit supports having a ",(0,i.jsx)(n.code,{children:"testconfig.json"})," file within your test project."]}),"\n",(0,i.jsxs)(n.p,{children:["This can be used to store key-value configuration pairs. To retrieve these within tests, use the static method ",(0,i.jsx)(n.code,{children:"TestContext.Configuration.Get(key)"}),"."]}),"\n",(0,i.jsx)(n.admonition,{type:"note",children:(0,i.jsxs)(n.p,{children:["TUnit delegates configuration loading to Microsoft.Testing.Platform's ",(0,i.jsx)(n.code,{children:"IConfiguration"}),". The platform's actual filename convention is ",(0,i.jsx)(n.code,{children:"[AppName].testconfig.json"})," (for example, ",(0,i.jsx)(n.code,{children:"MyTests.testconfig.json"}),"), which is what gets discovered and loaded at runtime. The name ",(0,i.jsx)(n.code,{children:"testconfig.json"})," is used throughout this page as a convenient shorthand."]})}),"\n",(0,i.jsx)(n.h2,{id:"example",children:"Example"}),"\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.code,{children:"testconfig.json"})}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-json",children:'{\n "MyKey1": "MyValue1",\n "BaseUrl": "https://api.example.com",\n "Nested": {\n "MyKey2": "MyValue2"\n }\n}\n'})}),"\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.code,{children:"Tests.cs"})}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-csharp",children:'[Test]\npublic async Task Test()\n{\n var value1 = TestContext.Configuration.Get("MyKey1"); // "MyValue1"\n var value2 = TestContext.Configuration.Get("Nested:MyKey2"); // "MyValue2"\n\n await Assert.That(value1).IsEqualTo("MyValue1");\n}\n'})}),"\n",(0,i.jsx)(n.h2,{id:"missing-keys-and-files",children:"Missing Keys and Files"}),"\n",(0,i.jsxs)(n.p,{children:["If a key does not exist, ",(0,i.jsx)(n.code,{children:"Get"})," returns ",(0,i.jsx)(n.code,{children:"null"}),". If the ",(0,i.jsx)(n.code,{children:"testconfig.json"})," file is missing entirely, all calls to ",(0,i.jsx)(n.code,{children:"Get"})," return ",(0,i.jsx)(n.code,{children:"null"}),". There is no exception thrown in either case."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-csharp",children:'[Test]\npublic async Task Configuration_Returns_Null_For_Unknown_Key()\n{\n var value = TestContext.Configuration.Get("DoesNotExist");\n\n await Assert.That(value).IsNull();\n}\n'})}),"\n",(0,i.jsx)(n.h2,{id:"nested-key-syntax",children:"Nested Key Syntax"}),"\n",(0,i.jsxs)(n.p,{children:["Use a colon (",(0,i.jsx)(n.code,{children:":"}),") to access values nested inside JSON objects. The path follows the same convention as ",(0,i.jsx)(n.code,{children:"Microsoft.Extensions.Configuration"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-json",children:'{\n "Database": {\n "Connection": {\n "Timeout": "30"\n }\n }\n}\n'})}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-csharp",children:'var timeout = TestContext.Configuration.Get("Database:Connection:Timeout"); // "30"\n'})}),"\n",(0,i.jsx)(n.h2,{id:"typed-configuration",children:"Typed Configuration"}),"\n",(0,i.jsxs)(n.p,{children:["All values are returned as ",(0,i.jsx)(n.code,{children:"string?"}),". Convert to the required type as needed:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-csharp",children:'[Test]\npublic async Task Respects_Configured_Timeout()\n{\n var rawTimeout = TestContext.Configuration.Get("Database:Connection:Timeout");\n var timeout = int.Parse(rawTimeout!);\n\n await Assert.That(timeout).IsEqualTo(30);\n}\n'})})]})}function u(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}},28453(e,n,t){t.d(n,{R:()=>a,x:()=>r});var s=t(96540);const i={},o=s.createContext(i);function a(e){const n=s.useContext(o);return s.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function r(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:a(e.components),s.createElement(o.Provider,{value:n},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.