Precept 0.10.0

Environment variables

Every Precept setting can be set from the environment. This page is the complete list, generated by hand from the settings classes themselves — PreceptSettings, the module settings, and the three reporters' — and it is the reference a pipeline is written against.

The rule#

PreceptSettings.Load builds its configuration from three sources, each overriding the last:

precept.json  →  precept.{environment}.json  →  PRECEPT_* environment variables

The variable name is the configuration key, prefixed with PRECEPT_, with every : written as a double underscore:

Configuration key Variable
MaxParallelism PRECEPT_MAXPARALLELISM
Web:Headless PRECEPT_WEB__HEADLESS
Reporting:Teams:WebhookUrl PRECEPT_REPORTING__TEAMS__WEBHOOKURL

Names are case-insensitive: PRECEPT_WEB__HEADLESS and Precept_Web__Headless are the same variable. The __ is not decoration — .NET's environment provider rewrites it to : before matching, which is the only way to express a nested key in a name that has to be a valid environment variable.

PRECEPT_ is a namespace with an owner. Everything carrying that prefix is stripped of it and bound as a settings key, whether it names a real setting or not. Do not use it for a variable of your own — a pipeline's scratch values, a script's working state — because a name that happens to collide with a real key changes the run and nothing says so.

Value formats#

Type Accepts Rejects
Boolean true, True, false, False 1, 0, yes, no — see below
Integer 8, -1 anything non-numeric
Enum the member name, any casing: Class, test, FailureOrFlaky a number
String anything, including empty
Dictionary one variable per entry; the last segment is the key
List one variable per element, indexed from zero

A boolean must be spelled out. The binder uses bool.Parse, and 1 is not a boolean to it — it fails the run before a test starts, not quietly:

Unhandled exception. System.InvalidOperationException: Failed to convert configuration value '1'
at 'IsContinuousIntegration' to type 'System.Boolean'.

Worth knowing because a CI system that writes 1 for a checkbox is not unusual, and because CI=1 — the one variable Precept reads for its value rather than binding — does work.

A dictionary entry is a variable of its own, and the key is whatever follows the last __:

PRECEPT_API__DEFAULTHEADERS__X-TENANT=acme        # Api:DefaultHeaders["X-Tenant"]
PRECEPT_REPORTING__TEAMS__FACTS__BRANCH=main      # Reporting:Teams:Facts["Branch"]

A list element is indexed:

PRECEPT_TESTDATA__FILES__0=testdata.json
PRECEPT_TESTDATA__FILES__1=vehicles.json

An empty value is a value. PRECEPT_FILTER__EXCLUDE= does not mean "the pipeline has no opinion"; it sets the exclude expression to empty and so overrides whatever precept.{environment}.json asked for. Unset the variable rather than blanking it.

The run#

Bound onto PreceptSettings itself, so these have no section in their name.

Variable Default What it does
PRECEPT_ENVIRONMENT local The environment, and so which precept.{env}.json overlay loads. Read directly rather than through the binder — see below.
PRECEPT_ISCONTINUOUSINTEGRATION detected Whether this run counts as a build agent's. Decides whether CI-only reporters start at all.
PRECEPT_MAXPARALLELISM processor count Units of work in flight at once. 0 or less means the processor count. A ceiling, not a target — a run cannot exceed the units it has.
PRECEPT_PARALLELSCOPE Class Class or Test. What one unit of parallel work is.
PRECEPT_RUNTIMEOUTMINUTES 720 How long the whole run may take before what is still running is cut off and the rest is reported as not run. 0 removes the limit. Not enforced under a debugger.
PRECEPT_DEFAULTTIMEOUTMILLISECONDS 0 Per-test timeout for tests with no [Timeout]. 0 — the default — sets none; the run limit above is what bounds a run. Not enforced under a debugger.
PRECEPT_DEFAULTRETRIES 0 Reruns of a failing test with no [Retry]. 0 reruns nothing; 2 means a test may run three times in all.
PRECEPT_ARTIFACTDIRECTORY PreceptArtifacts Where screenshots, traces and attachments are written. Relative to the test binary unless rooted. Keep it short — Windows caps a full path at 260 characters, past which the artifact is written but the TRX writer's copy of it fails.

Filter#

Tests this run pretends were never written: absent from the run, from the report and from a test explorer's tree, rather than reported as skipped. This is the environment's own policy, and it is applied in discovery — --precept-filter cannot select a test it has removed.

Variable Default What it does
PRECEPT_FILTER__INCLUDE unset Run only tests matching this expression.
PRECEPT_FILTER__EXCLUDE unset Remove tests matching it, whatever Include says.

Both are single expressions rather than lists, precisely so a later source can replace one outright. The expression language is the same one --precept-filter takes.

Web#

Variable Default What it does
PRECEPT_WEB__BASEURL unset Base address relative navigations resolve against.
PRECEPT_WEB__BROWSER chromium chromium, firefox or webkit.
PRECEPT_WEB__ARGS unset Extra command-line arguments for the browser executable, as one command line: --no-sandbox --disable-dev-shm-usage. Split on whitespace outside quotes, and either quote character groups — --user-agent='Mozilla/5.0 (X11)' is one argument. Passed to the browser process, so it applies to the whole run and not to one test; the flags are the engine's own and an unrecognised one is ignored rather than reported.
PRECEPT_WEB__HEADLESS true Run without a visible browser window.
PRECEPT_WEB__TIMEOUTMILLISECONDS 30000 Default timeout for element and navigation waits.
PRECEPT_WEB__ASSERTIONTIMEOUTMILLISECONDS 5000 How long a web-first assertion retries. Separate from the above on purpose: an action waiting half a minute is patience, an assertion doing it is a suite that takes half a minute to say what broke.
PRECEPT_WEB__SLOWMOMILLISECONDS 0 Slow each Playwright operation down, for debugging.
PRECEPT_WEB__RESOLUTION unset The viewport as one value: 720p, 900p, 1080p, 1440p, 2160p, the aliases hd, fhd, qhd, uhd, 4k, or a pair such as 1366x768. Decides the viewport outright when set, so a pipeline naming one need not also clear the two numbers below. An unrecognised value fails the run rather than picking a size for you.
PRECEPT_WEB__VIEWPORTWIDTH 1280 Viewport width in pixels. Used when no resolution is named.
PRECEPT_WEB__VIEWPORTHEIGHT 800 Viewport height in pixels. Used when no resolution is named.
PRECEPT_WEB__SCREENSHOTONFAILURE true Capture a screenshot when a test fails.
PRECEPT_WEB__TRACEONFAILURE true Record a Playwright trace, attached on failure.
PRECEPT_WEB__VIDEOONFAILURE false Record a video of each test, attached on failure.

Api#

Variable Default What it does
PRECEPT_API__BASEURL unset Base address relative request paths resolve against.
PRECEPT_API__TIMEOUTMILLISECONDS 30000 Request timeout.
PRECEPT_API__LOGPAYLOADS true Attach full request and response bodies to the test log.
PRECEPT_API__DEFAULTHEADERS__<NAME> none A header sent with every request. One variable per header.
PRECEPT_API__XMLNAMESPACES__<PREFIX> none A namespace URI an XPath prefix resolves to when reading an XML response. One variable per prefix.

Assertions#

Read only by assertions that wait — those built with Assert.Eventually. An assertion over a value already in hand is decided immediately and reads none of this.

Variable Default What it does
PRECEPT_ASSERTIONS__TIMEOUTMILLISECONDS 5000 How long a waiting assertion keeps re-reading its value.
PRECEPT_ASSERTIONS__POLLINTERVALMILLISECONDS 100 How long to wait between attempts.

Grpc#

Variable Default What it does
PRECEPT_GRPC__ADDRESS unset The service address.
PRECEPT_GRPC__DEADLINEMILLISECONDS 30000 Per-call deadline.
PRECEPT_GRPC__ALLOWINSECURE true Permit an unencrypted http:// channel.
PRECEPT_GRPC__MAXRECEIVEMESSAGESIZE 4194304 Largest message accepted, in bytes.
PRECEPT_GRPC__LOGMESSAGES true Attach request and response messages to the test log.

TestData#

These configure the module. The data itself lives in its own files, and has a namespace of its own.

Variable Default What it does
PRECEPT_TESTDATA__DIRECTORY . Directory the data files are read from, relative to the test binary unless rooted.
PRECEPT_TESTDATA__FILES__<n> testdata.json Data files, in increasing order of precedence. One variable per element.
PRECEPT_TESTDATA__ENVIRONMENTOVERLAY true Load the {file}.{environment}.json overlay beside each file.
PRECEPT_TESTDATA__LEASETIMEOUTMILLISECONDS 30000 How long LeaseAsync waits for a pooled item. Overridable per pool.

Reqnroll#

Read as the first scenario starts, by Precept.Reqnroll's runtime plugin. The section's other keys — FEATURELANGUAGE, ALLOWROWTESTS, ADDNONPARALLELIZABLEMARKERFORTAGS, ALLOWDEBUGGENERATEDFILES, DISABLEFRIENDLYTESTNAMES — shape the generated code and are read from the project's precept.json while the feature files compile, so a variable cannot reach them; see configuring Reqnroll.

Variable Default What it does
PRECEPT_REQNROLL__BINDINGCULTURE unset Culture step arguments are converted with. Unset means the feature language.
PRECEPT_REQNROLL__STOPATFIRSTERROR false Skip the remaining steps of a scenario once one has failed.
PRECEPT_REQNROLL__MISSINGORPENDINGSTEPSOUTCOME Pending Outcome of a scenario with a missing or pending step: Pending, Inconclusive, Ignore or Error.
PRECEPT_REQNROLL__OBSOLETEBEHAVIOR Warn What a step bound to an [Obsolete] definition does: None, Warn, Pending or Error.
PRECEPT_REQNROLL__TRACESUCCESSFULSTEPS true Write every passing step to the test's log.
PRECEPT_REQNROLL__TRACETIMINGS false Log how long each step took.
PRECEPT_REQNROLL__MINTRACEDDURATIONMILLISECONDS 100 Shortest step duration worth a timing line.
PRECEPT_REQNROLL__STEPDEFINITIONSKELETONSTYLE CucumberExpressionAttribute Shape of the skeleton logged for a step with no definition.
PRECEPT_REQNROLL__BINDINGASSEMBLIES__<n> none Assemblies searched for bindings besides the test assembly. One variable per element.

ConnectionStrings#

One variable per named connection, the name being whatever follows the last __. Db.Query(...) uses Default unless told otherwise.

PRECEPT_CONNECTIONSTRINGS__DEFAULT='Host=db.stage;Database=app;Username=tests;Password=…'
PRECEPT_CONNECTIONSTRINGS__WAREHOUSE='Server=…'

A connection string holds a password, so this is a variable and not a line in precept.json.

Reporting#

The pipeline itself. What each reporter does with the events is in its own subsection below.

Variable Default What it does
PRECEPT_REPORTING__ENABLED true Turns the whole pipeline off. Nothing is constructed and no queue is started.
PRECEPT_REPORTING__CIONLY unset Overrides where every reporter runs. true confines the lot to a build agent, false lets the lot run anywhere. Unset, each reporter decides, and the three shipped ones are CI-only.
PRECEPT_REPORTING__QUEUECAPACITY 4096 Events that may wait for one reporter before further ones are dropped. Drops are counted and reported at the end of the run.
PRECEPT_REPORTING__SHUTDOWNTIMEOUTSECONDS 60 How long the run waits, after the last test, for reporters to finish sending.
PRECEPT_REPORTING__MAXFAILURES 10 How many times one reporter may throw before it is dropped for the rest of the run. A destination that is down should cost one message, not one per test.
PRECEPT_REPORTING__MAXTRACKEDFAILURES 100 How many failed tests the run summary carries. The failure count is exact either way.

PRECEPT_REPORTING__CIONLY=false is the single variable that makes a laptop report for one run. From there a missing API key is an error, because you have just asked that machine to report.

Reporting:ReportPortal#

Variable Default What it does
PRECEPT_REPORTING__REPORTPORTAL__ENABLED true Report to ReportPortal at all.
PRECEPT_REPORTING__REPORTPORTAL__CIONLY true Report only from a build agent.
PRECEPT_REPORTING__REPORTPORTAL__ENDPOINT unset Base address, without the /api path.
PRECEPT_REPORTING__REPORTPORTAL__PROJECT unset The project the launch belongs to.
PRECEPT_REPORTING__REPORTPORTAL__APIKEY unset Credential. The account the launch is reported under.
PRECEPT_REPORTING__REPORTPORTAL__LAUNCHNAME assembly name Name of the launch.
PRECEPT_REPORTING__REPORTPORTAL__DESCRIPTION a line naming the environment Description on the launch. On an Azure DevOps agent a link to the build is appended either way.
PRECEPT_REPORTING__REPORTPORTAL__LAUNCHMODE DEFAULT DEFAULT for a launch everyone sees, DEBUG for one kept out of the project's statistics.
PRECEPT_REPORTING__REPORTPORTAL__ATTRIBUTES__<KEY> none An attribute on the launch. One variable per attribute; the environment is added automatically.
PRECEPT_REPORTING__REPORTPORTAL__REPORTCATEGORIESASATTRIBUTES true Report each test's tags as attributes of its item.
PRECEPT_REPORTING__REPORTPORTAL__LOGMESSAGES true Send the lines a test logged as log entries.
PRECEPT_REPORTING__REPORTPORTAL__ATTACHARTIFACTS true Upload screenshots, traces and other captured files.
PRECEPT_REPORTING__REPORTPORTAL__ATTACHONSUCCESS false Attach artifacts of passing tests too.
PRECEPT_REPORTING__REPORTPORTAL__MAXATTACHMENTBYTES 8388608 Largest artifact uploaded. A trace of a long scenario runs to tens of megabytes and is rarely what anyone opens from a dashboard.
PRECEPT_REPORTING__REPORTPORTAL__RERUN false Report this launch as a rerun, so ReportPortal merges it into the previous one.
PRECEPT_REPORTING__REPORTPORTAL__RERUNOF unset Name or UUID of the launch being rerun.
PRECEPT_REPORTING__REPORTPORTAL__TIMEOUTSECONDS 60 How long any single call may take.

Reporting:Teams#

Variable Default What it does
PRECEPT_REPORTING__TEAMS__ENABLED true Post at all.
PRECEPT_REPORTING__TEAMS__CIONLY true Post only from a build agent.
PRECEPT_REPORTING__TEAMS__WEBHOOKURL unset Credential. The channel's incoming webhook, from its Workflows connector. Anyone holding it can post to the channel.
PRECEPT_REPORTING__TEAMS__TITLE assembly name Heading on the card.
PRECEPT_REPORTING__TEAMS__VARIANT Summary Card layout: Summary, Compact, Detailed (adds the failures), or Pipeline (adds the build).
PRECEPT_REPORTING__TEAMS__NOTIFYON Always Always, Failure, or FailureOrFlaky — the last of which also posts when a test only passed on a retry.
PRECEPT_REPORTING__TEAMS__IMAGEURL unset An image for the card — a pipeline status badge, a logo. Must be reachable by every reader's Teams client without signing in.
PRECEPT_REPORTING__TEAMS__IMAGEPLACEMENT Thumbnail Where it goes: Thumbnail, Banner, or None to drop it without losing the URL.
PRECEPT_REPORTING__TEAMS__IMAGEALTTEXT Run image What a screen reader says in its place.
PRECEPT_REPORTING__TEAMS__MAXFAILURESLISTED 10 How many failed tests the Detailed card names before it gives the remaining count.
PRECEPT_REPORTING__TEAMS__MAXFAILUREMESSAGELENGTH 300 Longest a failure message may be on the Detailed card.
PRECEPT_REPORTING__TEAMS__RUNURL the agent's build Where the card's button goes. Left unset, the Azure DevOps build or GitHub Actions run the suite is executing in.
PRECEPT_REPORTING__TEAMS__RUNURLTITLE Open build <number> Label on that button; Open the run when no build was detected.
PRECEPT_REPORTING__TEAMS__LINKTOOTHERREPORTS true A button per destination this run also reported to — the ReportPortal launch, the Azure DevOps test run.
PRECEPT_REPORTING__TEAMS__FACTS__<KEY> none An extra row on the card's fact list — a build number, a branch, a browser.
PRECEPT_REPORTING__TEAMS__TIMEOUTSECONDS 30 How long the post may take before it is given up on.

Reporting:AzureDevOps#

Variable Default What it does
PRECEPT_REPORTING__AZUREDEVOPS__ENABLED true Create a test run at all.
PRECEPT_REPORTING__AZUREDEVOPS__CIONLY true File results only from a build agent.
PRECEPT_REPORTING__AZUREDEVOPS__ORGANIZATIONURL unset Required when it runs. For example https://dev.azure.com/acme.
PRECEPT_REPORTING__AZUREDEVOPS__PROJECT unset Required when it runs. The team project the run belongs to.
PRECEPT_REPORTING__AZUREDEVOPS__PERSONALACCESSTOKEN unset Credential. Needs Test Management (read & write); Work Items (read) is worth adding but never required. From a pipeline this is normally $(System.AccessToken).
PRECEPT_REPORTING__AZUREDEVOPS__RUNNAME assembly name and environment Name of the test run.
PRECEPT_REPORTING__AZUREDEVOPS__RUNBY whoever the token is Who the run is attributed to — an identity GUID, or a unique name for Azure DevOps to resolve.
PRECEPT_REPORTING__AZUREDEVOPS__BUILDID unset Associates the run with a build, so it appears on that build's Tests tab. Unrelated to RunBy.
PRECEPT_REPORTING__AZUREDEVOPS__TESTPLANID unset Files the run under a plan, making it a planned run — which accepts a result only when it fills a test point.
PRECEPT_REPORTING__AZUREDEVOPS__TESTSUITEID unset Narrows a planned run to one suite's points.
PRECEPT_REPORTING__AZUREDEVOPS__TESTCASETAGPREFIX tc: Tag prefix naming the Test Case work item: @tc:41207. Empty reads ids from the title only.
PRECEPT_REPORTING__AZUREDEVOPS__TESTCASETITLEPATTERN ^\s*(?:\[(?<id>\d+)\]\s*)+ Regular expression with a group named id, read from the test's title. Empty reads ids from tags only.
PRECEPT_REPORTING__AZUREDEVOPS__ONLYMAPPEDTESTS false Report only tests that name a Test Case work item. A test plan implies it.
PRECEPT_REPORTING__AZUREDEVOPS__BATCHSIZE 100 Results per call.
PRECEPT_REPORTING__AZUREDEVOPS__ATTACHARTIFACTS true Upload each failed test's captured files.
PRECEPT_REPORTING__AZUREDEVOPS__MAXATTACHMENTBYTES 8388608 Largest artifact uploaded. Azure DevOps itself caps an attachment at 100 MB.
PRECEPT_REPORTING__AZUREDEVOPS__APIVERSION 7.1 REST API version the calls ask for.
PRECEPT_REPORTING__AZUREDEVOPS__TIMEOUTSECONDS 60 How long any single call may take.

Sections of your own#

The rule is not special to Precept's own settings. A class registered with services.AddPreceptSettings<AuthSettings>() binds from the Auth section — the type's name without a Precept prefix or a Settings/Options/Configuration/Config suffix — and so reads:

PRECEPT_AUTH__TOKENENDPOINT=https://id.acme.com/token
PRECEPT_AUTH__CLIENTID=test-runner
PRECEPT_AUTH__CLIENTSECRET=…            # from the build's secret store, never from precept.json

An explicit section name follows the same shape: services.AddPreceptSettings<AuthSettings>("Tenants:Primary") reads PRECEPT_TENANTS__PRIMARY__CLIENTSECRET.

Test data values: a second namespace#

Precept.TestData builds a configuration of its own for the data, and adds the environment to it under the prefix PRECEPT_TESTDATA__. That is how a password stays out of a data file:

PRECEPT_TESTDATA__USERS__ADMIN__PASSWORD=…      # users:admin:password

The path after the prefix is the data path, lowercased by convention and matched case-insensitively, so this overrides users.admin.password from testdata.json and its overlay.

One caveat, and only for variables. The two namespaces overlap: PRECEPT_TESTDATA__… is seen by the main configuration as well, under the TestData section — which is where the module's own settings live. So the four names in TestData mean both things at once, in both directions. PRECEPT_TESTDATA__DIRECTORY=/data sets the module's directory and injects a data value at path directory; overriding a root data path called files from a variable would also rewrite the module's file list.

Data files are unaffected — they are read by the test data store alone, so a JSON key named directory or files in testdata.json collides with nothing. It is only the environment that sees both. If a data set has a root key with one of those four names and it has to be overridable from CI, nest it a level down.

Data files can also read arbitrary variables through a token, which is a different mechanism again — it is resolved where the value is used, not bound to a setting:

{ "users": { "admin": { "note": "{{env:BUILD_BUILDNUMBER}}" } } }
{ "users": { "admin": { "note": "{{env:BUILD_BUILDNUMBER|local}}" } } }

Without a |fallback an unset variable fails the test that reads it, which is the difference between a suite that runs on a developer's machine and one that only runs on the agent.

Variables Precept reads but does not own#

Variable Read for
PRECEPT_ENVIRONMENT The environment. Read directly, ahead of the configuration chain, because the answer decides which overlay that chain loads.
DOTNET_ENVIRONMENT The same, for a suite that already sets it. Consulted only when PRECEPT_ENVIRONMENT says nothing.
CI CI detection — and this one is read for its value: false, 0 and no mean what they say, anything else present counts as CI.
TF_BUILD, BUILD_BUILDID, GITHUB_ACTIONS, GITLAB_CI, JENKINS_URL, HUDSON_URL, TEAMCITY_VERSION, bamboo_buildKey, BAMBOO_BUILDKEY, CIRCLECI, APPVEYOR, BUILDKITE, DRONE, CODEBUILD_BUILD_ID, TRAVIS CI detection. Each is set by one system and by nothing else, so its mere presence is the whole test. BUILD_BUILDID is there for the self-hosted Azure DevOps agent that exports the build variables without TF_BUILD.
BUILD_BUILDID, BUILD_BUILDNUMBER, SYSTEM_TEAMFOUNDATIONCOLLECTIONURI, SYSTEM_TEAMFOUNDATIONSERVERURI, SYSTEM_TEAMPROJECT The ReportPortal reporter builds a link back to the Azure DevOps build from these. Nothing to configure.

The environment is not the only source for either of those two things. The full resolution order for the environment is --precept-environment, PRECEPT_ENVIRONMENT, DOTNET_ENVIRONMENT, the PreceptEnvironment MSBuild property, "Environment" in precept.json, then local; and IsContinuousIntegration is an ordinary setting whose default is the detection above, so PRECEPT_ISCONTINUOUSINTEGRATION and an overlay both override it.

What has no variable#

--precept-filter is a command-line option and nothing else. It says what this run selects rather than how the suite behaves, which is why it is not a setting — and why there is no PRECEPT_FILTER. That name would be misleading if it existed: Filter is the section holding Filter:Include and Filter:Exclude, which is the other filter, applied in discovery.

For an IDE with nowhere to type a runner argument, testconfig.json beside the project supplies command-line options from a file:

{ "commandLineOptions": { "precept-environment": "stage" } }

.runsettings does nothing here at all — it is VSTest-only, and Precept runs on Microsoft.Testing.Platform.

Working with them#

Set one for a single run:

PRECEPT_MAXPARALLELISM=3 dotnet run --project tests/Acme.Tests -- --list-tests

Every run states the two things most often set wrongly, so a variable that did not take effect is visible in the first line of output:

[Precept] Environment 'stage'. Up to 3 classes in parallel. Filter excluded 5 of 233 tests.
[Precept] There is no 'precept.stag.json' beside the test binary, so the run is using
          'precept.json' alone. Check the environment name if that is not what you meant.

The unit is classes or tests depending on ParallelScope, the filter clause appears only when the configuration filter actually removed something, and the missing-overlay warning is suppressed for local — which has no overlay by design.

On CI, keep endpoints and project names in precept.json where they can be reviewed, and pass only the credentials — the four marked Credential above, plus ConnectionStrings — from the build's secret store. Running a suite on CI is a worked Azure Pipelines example doing exactly that.

Precept 0.10.0 · MIT · © 2026

Esc