bitwarden/server
Testing
Bitwarden Server uses xUnit for both unit and integration tests, with NSubstitute for mocking, FluentAssertions for expressive asserts, and AutoFixture (Bit.Test.Common) for test-data generation.
Test projects
There are 30+ test projects, paired with their production project. Major ones:
| Test project | Covers |
|---|---|
test/Core.Test/ |
Domain layer (src/Core/) — services, commands, queries. |
test/Api.Test/ |
Controllers and request validators in src/Api/. |
test/Api.IntegrationTest/ |
End-to-end HTTP tests via WebApplicationFactory. |
test/Identity.Test/ and test/Identity.IntegrationTest/ |
OAuth grants, 2FA token providers. |
test/Events.Test/, test/EventsProcessor.Test/, test/Events.IntegrationTest/ |
Event API + queue drain. |
test/Infrastructure.IntegrationTest/ |
Dapper + EF repositories run against real databases. |
test/IntegrationTestCommon/ |
Shared helpers (AbstractIdentityTokenProvider, Factories, fixtures). |
bitwarden_license/test/Commercial.Core.Test/ |
Provider, Secrets Manager, and licensed-billing logic. |
bitwarden_license/test/SSO.Test/, Sso.IntegrationTest/, Scim.Test/, Scim.IntegrationTest/ |
Commercial Sso/Scim hosts. |
Test projects link the production project they cover and Bit.Test.Common (test/Common/).
Unit tests
Unit tests are fast and have no external dependencies. They follow this layout:
public class CipherServiceTests
{
[Theory, BitAutoData]
public async Task SaveAsync_NewCipher_PersistsAndPublishesEvent(
SutProvider<CipherService> sutProvider,
Cipher cipher,
Guid userId)
{
// Arrange
sutProvider.GetDependency<ICipherRepository>()
.CreateAsync(cipher).Returns(cipher);
// Act
await sutProvider.Sut.SaveAsync(cipher, userId);
// Assert
await sutProvider.GetDependency<IEventService>()
.Received(1).LogCipherEventAsync(cipher, EventType.Cipher_Created);
}
}SutProvider<T> (Bit.Test.Common.AutoFixture.Attributes) auto-substitutes every constructor dependency with NSubstitute mocks. BitAutoData seeds AutoFixture with realistic Bitwarden domain types (matching server validations).
Integration tests
Integration tests boot a full ASP.NET Core test host. The shared base class is Bit.IntegrationTestCommon.Factories.<App>ApplicationFactory<TStartup>. Each factory:
- Replaces external services (Stripe, Push, Mail) with NoOp implementations.
- Optionally points the database at a per-test transient instance using TestContainers (the integration tests under
test/Infrastructure.IntegrationTestrun against real MySQL / Postgres / SQLite via theMySqlIntegrationTest,PostgresIntegrationTest,SqliteIntegrationTestcollection fixtures). - Authenticates via helper
LoginHelperto mint real JWTs from the Identity stack.
The test database lifecycle is managed by BaseRepositoryTests and DatabaseFixture derivatives.
Database tests
test/Infrastructure.IntegrationTest/ runs the same test cases against all four database providers using xUnit collection fixtures. This is how the project guarantees Dapper and EF stay in sync.
Coverage
The codecov.yml configuration is minimal (codecov: { require_ci_to_pass: yes }) — coverage is collected by coverlet.collector (currently being bumped to v10) but not strictly enforced as a gate. The expectation is that domain code (src/Core/) has high unit-test coverage and that public controllers have at least one happy-path integration test.
Running tests
# Unit tests for a single project
dotnet test test/Core.Test/Core.Test.csproj
# Whole solution (skips integration unless DB is up)
dotnet test bitwarden-server.sln --filter "Category!=Integration"
# Integration tests (require Docker compose `cloud` profile)
docker compose -f dev/docker-compose.yml --profile cloud up -d
dotnet test test/Api.IntegrationTest/Api.IntegrationTest.csprojThe .github/workflows/test.yml workflow runs the full unit-test set on every push, and .github/workflows/test-database.yml exercises the DB-portability tests against MS SQL, MySQL, Postgres, and SQLite in parallel.
Writing new tests
- Follow the
Test_<Method>_<Condition>_<Expected>naming convention. - Prefer
[Theory, BitAutoData]over[Fact]for production code that is naturally parameterised. - Use
SutProvider<T>to avoid hand-wiring NSubstitute mocks. - For repositories, write tests in
test/Infrastructure.IntegrationTest/so they run against every supported database.
Performance / load tests
The perf/ directory contains k6 scripts (perf/load-tests/) that the .github/workflows/load-test.yml runs against ephemeral environments. They are not part of the per-PR test suite.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.