Test code that uses the plugin

Your view models and services depend on INearbyConnectivity, so fake that interface and test them on the desktop. You need no device and no emulator.

Put your view models and services in a net10.0 class library, and reference that library from both the app and the tests. A MAUI app targets net10.0-android and net10.0-ios, which a desktop test project cannot reference.

Target net10.0 from the test project

The package also has a plain net10.0 target, so a test project can reference it. That target exists for compilation, not for use at run time. On it, CheckAvailabilityAsync reports NearbyAvailability.Unsupported and every start method throws NearbyException.

Fake the interfaces

INearbyConnectivity, INearbyAdvertiser, and INearbyDiscoverer are public interfaces, so a mocking library can fake them. The examples use NSubstitute.

var nearby = Substitute.For<INearbyConnectivity>();

To test a failure path, return the real exception type:

nearby.Discoverer.StartAsync(Arg.Any<CancellationToken>())
    .Returns(Task.FromException(new NearbyException("Bluetooth is off.")));

The real session raises its events on a thread-pool thread. A fake raises them on the test's thread. Code that dispatches to the UI thread works with both.

Build devices for the fake to report

NearbyDevice has a public constructor for tests. Build a device with the status that your test needs, and raise DeviceChanged with it.

var ana = new NearbyDevice("a1", new NearbyPresence { DisplayName = "Ana" })
{
    Status = NearbyDeviceStatus.Connected,
};

nearby.Devices.Returns([ana]);
nearby.DeviceChanged += Raise.Event<EventHandler<NearbyDeviceChange>>(
    nearby, new NearbyDeviceChange(NearbyDeviceChangeAction.Added, ana));

A device built this way belongs to no session, so ConnectAsync and DisconnectAsync throw InvalidOperationException.

NearbyConnection and NearbyConnectionRequest are sealed, and their constructors are internal. A test cannot create them, and a mocking library cannot fake them. To test code that sends or accepts, put that call behind a method that your view model owns. Then fake that method.

Register the plugin in a bare host

AddNearbyConnectivity also exists on IServiceCollection, for a host without a MauiAppBuilder. It works where nobody called AddLogging. It also registers a TimeProvider if the host registered none. It validates the options, so an invalid ServiceId throws at the registration call. The first registration wins: a second call registers nothing, and its options are ignored.

var services = new ServiceCollection();
services.AddNearbyConnectivity(o => o.ServiceId = "tests");

Next