Testing Domain Services Code¶
Domain Services components are plain .NET classes — services take repository interfaces, repositories return Maybe<T>. There's no framework magic to work around in tests.
Testing a service with JsonRepository (no mocks)¶
JsonRepository<TEntity, TId> writes to a real file. In tests, point it at a temp path so each test gets a clean slate and nothing leaks to disk permanently.
using DHI.Services;
using DHI.Services.Jobs;
using Xunit;
public class JobServiceTests : IDisposable
{
private readonly string _tempFile;
private readonly JobService<object, string> _svc;
public JobServiceTests()
{
_tempFile = Path.GetTempFileName();
var repo = new JobRepository(_tempFile);
_svc = new JobService<object, string>(repo, null, null);
}
[Fact]
public void Add_ThenGet_ReturnsJob()
{
var job = new Job<Guid, string>(Guid.NewGuid(), "RunReport", "admin");
_svc.Add(job);
var found = _svc.TryGet(job.Id, out var result);
Assert.True(found);
Assert.Equal("RunReport", result.TaskId);
}
public void Dispose() => File.Delete(_tempFile);
}
Key points:
- Path.GetTempFileName() creates a unique file each test run.
- IDisposable.Dispose cleans up — xUnit calls it after each test class instance.
- No async, no mocking library, no in-memory database setup needed.
Testing a service with a mock repository¶
When you want to test service logic in isolation (e.g., lifecycle events, guards), mock the repository interface instead. Any mocking library works; example uses NSubstitute:
using NSubstitute;
using DHI.Services;
public class SensorServiceTests
{
[Fact]
public void Add_RaisesAddedEvent()
{
var repo = Substitute.For<ISensorRepository>();
var svc = new SensorService(repo);
bool eventRaised = false;
svc.Added += (_, _) => eventRaised = true;
svc.Add(new Sensor(Guid.NewGuid(), "CTD #1", "mS/cm"));
Assert.True(eventRaised);
}
}
Mock the interface (ISensorRepository), not the concrete class. The service only depends on the interface.
Testing with multiple providers in the same test suite¶
If you have integration tests that run against different providers (JSON for CI, PostgreSQL for local), use a factory method or fixture that picks the repository based on an environment variable:
static ISensorRepository CreateRepo()
{
var conn = Environment.GetEnvironmentVariable("TEST_SENSOR_CONN");
if (string.IsNullOrEmpty(conn))
return new SensorRepository(Path.GetTempFileName());
// PostgreSQL or other provider
return new PgSensorRepository(conn);
}
This keeps tests runnable in CI without a database while still supporting full integration runs locally.
What not to test¶
- Don't test
JsonRepositoryserialization — that's the framework's job. - Don't test
Maybe<T>operators — they're tested in the core package. - Test your business logic — the guards you add, the lifecycle event handlers, the derived queries.