Osy#betaRun it on Osyrin: osyrin.com

Reference / Testing

[AllowNetwork] — a test that calls another machine

[Test, AllowNetwork] [Test, AllowNetwork("api.example.com", "*.example.org")]

A test calls no other machine unless it says so. An outbound call with no stub — `Http.*`, a typed client built on it, an SMTP send — is REFUSED when its host is not this machine, and the refusal names the URL and both ways out: stub the verb, or put `[AllowNetwork("host")]` on the test. Calls to localhost always go.

preview2 examples compiled by CItestinghttpintegration

Summary#

osy test runs your tests against this machine and nothing else. A call to another machine that no stub answers is refused, so a suite never passes or fails with somebody else's server, and never sends a request to it from every machine that runs it. The refusal says exactly what to do:

`Http.Get` to https://rates.example.com/v1/latest.json was refused: a test calls no other machine unless it says so.
Stub it in the test — `Http.Get.Stub(url => new HttpResponse { StatusCode = 200, Body = "…" });` — or allow the
host on the test: `[Test, AllowNetwork("rates.example.com")]`. Calls to this machine (localhost) always go.

[AllowNetwork] is that second way out: the test that exists to prove the real endpoint works says so, and makes the call.

Signature#

[Test, AllowNetwork]                                    // every host
[Test, AllowNetwork("api.example.com")]                 // this host
[Test, AllowNetwork("api.example.com", "*.example.org")] // these hosts; `*.` = the domain's subdomains

Description#

  • What is refused: a call that is really about to leave — Http.Get/Post/Put/Patch/Delete, the streaming and file verbs, every typed client built on them, and the mail transports — whose host is not this machine. A stubbed call never gets that far, so a stub is always enough.
  • What always goes: localhost, any *.localhost name, and the loopback addresses (127.0.0.1, ::1). A test that stands up its own server, or calls the app's own address, is unaffected.
  • Both halves of a test. The rule reaches the server calls a test CAUSES — the function a Ui.Click runs, an Api.* call — not only the lines in its body.
  • A caught refusal still fails the test. Most apps wrap a fetch in try/catch and show the last value when it fails, so the refusal could be swallowed and the test pass on the fallback. The runner keeps every refusal and fails the test on it whatever the code did with the exception.
  • A [TestFixture] calls no other machine either, and takes no [AllowNetwork]: a fixture builds the world every test forks from. Stub the call in the fixture — every forked test is then answered the same way — and let the test that must reach the real host make the call itself.
  • Only on a [Test]. Anywhere else it is a compile error: outside a test nothing is stubbed and nothing refused.
  • Text only. Each host is a string literal (or literals joined with +), read before anything runs.

⚑ Leave one test that really calls out. A stub pins the PARSE of a response; only a real call proves the URL, the key and the response shape still work (Outbound calls in a test). [AllowNetwork] is how that test is written now.

using Osysharp.Http;

string RateBody() => Http.Get("https://rates.example.com/v1/latest.json").Body;

[Test]
void the_rate_is_read_from_the_answer() {
  Http.Get.Stub(url => new HttpResponse { StatusCode = 200, Body = "1.27" });
  Assert.Equal("1.27", RateBody());
}
using Osysharp.Http;

[Test, AllowNetwork("rates.example.com")]
void the_live_rates_endpoint_answers() {
  var r = Http.Get("https://rates.example.com/v1/latest.json");
  Assert.True(r.IsSuccess, "the live endpoint must answer");
}

See also#

Related

Outbound calls in a test

A `[Test]` answers an outbound call from a stub — `Http.<Verb>.Stub(url => …)` — or, when the test says so with…

[Test] / [TestFixture]

A test is an ordinary function marked [Test]. It runs against a throwaway clone of the app, so it may create rows and…

Calling your own REST API from a test

`Api.*` sends a real HTTP request to a route your own app publishes with `app.Apis`, from inside a `[Test]`, and…