https://github.com/testingbot/testingbot-dotnet
.NET/C# api client for TestingBot
https://github.com/testingbot/testingbot-dotnet
playwright testingbot testingbot-api
Last synced: 3 days ago
JSON representation
.NET/C# api client for TestingBot
- Host: GitHub
- URL: https://github.com/testingbot/testingbot-dotnet
- Owner: testingbot
- License: mit
- Created: 2026-06-04T15:47:11.000Z (about 1 month ago)
- Default Branch: main
- Last Pushed: 2026-06-05T07:59:57.000Z (about 1 month ago)
- Last Synced: 2026-06-05T08:15:26.106Z (about 1 month ago)
- Topics: playwright, testingbot, testingbot-api
- Language: C#
- Homepage: https://testingbot.com/support/api
- Size: 99.6 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# TestingBot .NET SDK
[](https://www.nuget.org/packages/TestingBot.Api/)
[](LICENSE)
The official .NET SDK for the [TestingBot](https://testingbot.com) REST API. Manage tests, builds,
app storage, tunnels, devices, screenshots, Codeless tests/suites, jobs, and team accounts from C#.
- **Async** all the way down, with `CancellationToken` support everywhere
- **Thread-safe** — construct one client and reuse it across your application
- **Strongly typed** models — no `dynamic`, no untyped dictionaries
- **Zero third-party dependencies** in the core package
- **Resilient** — automatic retries with exponential backoff and `Retry-After` support
- Optional **dependency-injection** package wiring up `IHttpClientFactory`
- Targets **.NET 8** and **.NET 9**
## Packages
| Package | Purpose |
|---|---|
| [`TestingBot.Api`](https://www.nuget.org/packages/TestingBot.Api/) | Core client and models (zero third-party dependencies) |
| [`TestingBot.Api.DependencyInjection`](https://www.nuget.org/packages/TestingBot.Api.DependencyInjection/) | `AddTestingBot()` for `IServiceCollection` / `IHttpClientFactory` |
## Installation
```bash
dotnet add package TestingBot.Api
# optional, for ASP.NET Core / Worker apps:
dotnet add package TestingBot.Api.DependencyInjection
```
## Authentication
Provide your [API key and secret](https://testingbot.com/members/user/edit) directly, or let the SDK
resolve them (in order) from a `~/.testingbot` file (`key:secret`), the `TESTINGBOT_KEY`/
`TESTINGBOT_SECRET` environment variables, or `TB_KEY`/`TB_SECRET`.
```csharp
using TestingBot.Api;
var client = new TestingBotClient("YOUR_KEY", "YOUR_SECRET");
// or resolve from ~/.testingbot or environment variables:
var client = TestingBotClient.FromEnvironment();
```
## Quick start
```csharp
using TestingBot.Api;
using var client = new TestingBotClient("YOUR_KEY", "YOUR_SECRET");
// Who am I?
var user = await client.User.GetAsync();
Console.WriteLine($"{user.Email} — {user.Seconds} seconds remaining");
// A single test, by id or WebDriver session id
var test = await client.Tests.GetAsync("a1b2c3d4-...");
Console.WriteLine($"{test.Name}: {test.Success}");
// Mark a test passed and tag it
await client.Tests.UpdateAsync(test.SessionId!, new TestUpdate
{
Success = true,
StatusMessage = "All assertions passed",
Build = "ci-1421",
Groups = ["smoke", "checkout"],
});
```
## Pagination
List endpoints return a `TestingBotPage` (the items plus `meta`), or you can stream every item
across all pages with `ListAllAsync`:
```csharp
// One page at a time
var page = await client.Tests.ListAsync(new TestListOptions { Count = 50, Build = "ci-1421" });
Console.WriteLine($"{page.Count} of {page.Meta.Total}");
// Or iterate everything (pages fetched lazily)
await foreach (var t in client.Tests.ListAllAsync(new TestListOptions { Build = "ci-1421" }))
{
Console.WriteLine($"{t.Id}: {t.Success}");
}
```
## Uploading apps (storage)
Uploads are streamed (never fully buffered in memory), support progress reporting, and use a longer
timeout than ordinary calls.
```csharp
var progress = new Progress(bytes => Console.WriteLine($"{bytes:N0} bytes sent"));
var app = await client.Storage.UploadAsync(new FileInfo("app.apk"), progress);
Console.WriteLine(app.AppUrl); // tb:// — pass as the `app` capability
// Replace the binary behind an app key without changing the tb:// URL:
await client.Storage.ReplaceAsync(app.AppKey!, new FileInfo("app-new.apk"));
```
## Codeless tests and jobs
```csharp
var run = await client.CodelessSuites.TriggerAsync(suiteId);
Job job = await client.Jobs.WaitForCompletionAsync(run.JobId, timeout: TimeSpan.FromMinutes(10));
Console.WriteLine(job.Success);
```
## Error handling
Every failure derives from `TestingBotException`; catch a specific type to react to a failure mode:
```csharp
try
{
await client.Tunnels.StopAsync(tunnelId);
}
catch (TestingBotRateLimitException ex)
{
await Task.Delay(ex.RetryAfter ?? TimeSpan.FromSeconds(30));
}
catch (TestingBotAuthenticationException)
{
Console.Error.WriteLine("Check your API key and secret.");
}
catch (TestingBotValidationException ex)
{
foreach (var error in ex.ValidationErrors)
{
Console.Error.WriteLine(error);
}
}
```
| Exception | HTTP status |
|---|---|
| `TestingBotAuthenticationException` | 401 |
| `TestingBotPaymentRequiredException` | 402 (insufficient credits) |
| `TestingBotForbiddenException` | 403 (read-only account / not admin) |
| `TestingBotNotFoundException` | 404 |
| `TestingBotValidationException` | 400 |
| `TestingBotRateLimitException` | 429 (carries `RetryAfter`) |
| `TestingBotApiException` | other 4xx/5xx, transport failures |
| `TestingBotConfigurationException` | missing credentials / invalid options (no HTTP) |
## Dependency injection
```csharp
using Microsoft.Extensions.DependencyInjection;
builder.Services.AddTestingBot(options =>
{
options.ApiKey = builder.Configuration["TestingBot:ApiKey"];
options.ApiSecret = builder.Configuration["TestingBot:ApiSecret"];
});
// or bind from configuration:
builder.Services.AddTestingBot(builder.Configuration.GetSection("TestingBot"));
```
Then inject `ITestingBotClient` anywhere. The client is registered as a singleton backed by
`IHttpClientFactory` with the SDK's authentication and retry handlers.
## Configuration
| Option | Default | Description |
|---|---|---|
| `BaseAddress` | `https://api.testingbot.com/v1/` | Override for sandbox/private deployments |
| `Timeout` | 100s | Per-request timeout for ordinary calls |
| `UploadTimeout` | 30m | Timeout for storage uploads |
| `MaxRetries` | 3 | Retries for transient failures (429/5xx) on idempotent requests |
| `RetryBaseDelay` | 1s | Base delay for exponential backoff |
| `RespectRetryAfter` | `true` | Honor the server's `Retry-After` header |
| `DefaultPageSize` | 50 | Page size used by `ListAllAsync` helpers |
## API coverage
`Tests`, `Builds`, `Storage`, `Screenshots`, `Tunnels`, `Devices`, `Browsers`, `CodelessTests`,
`CodelessSuites`, `Jobs`, `Team`, `User`, `Configuration`, plus `GetSharingAuthHash` for building
public share URLs.
## Contributing & development
```bash
dotnet build
dotnet test
dotnet format --verify-no-changes
```
Integration tests run only when `TESTINGBOT_KEY`/`TESTINGBOT_SECRET` are set and are otherwise skipped.
## License
MIT — see [LICENSE](LICENSE).