Skip to main content
Almedia.Initialize works inside the Unity Editor by falling back to a deterministic mock bridge that simulates the native layer. The mock fires OnStatusChanged / OnNotificationsReceived on a timer, so the UI can be exercised without a device build. It also simulates the screen lifecycle pair for every screen, modeling the webview linking strategy - starting linking from the popup’s CTA fires OnScreenPresented/OnScreenDismissed around the simulated flow so pause/resume wiring is exercisable in the editor, whereas production configured with the system-browser linking strategy fires no pair for linking. Static state and event subscribers are cleared on domain reload (RuntimeInitializeOnLoadMethod with SubsystemRegistration timing). This means:
  • Manual unsubscription in OnDestroy is not required to avoid leaking handlers across play-mode entry and exit - domain reload clears them. Within a single play session you must still unsubscribe, though: a MonoBehaviour that subscribes to an Almedia event and is then destroyed (scene unload, Destroy(gameObject)) stays registered. The next callback fires on the destroyed object and throws MissingReferenceException, which the bridge swallows to OnLog - it does not surface through OnErrorOccurred. Unsubscribe in OnDisable/OnDestroy for any component shorter-lived than the SDK.
  • Even with domain reload disabled in Edit → Project Settings → Editor → Enter Play Mode Options, subscriptions should still happen in Awake, because the SDK explicitly resets its own state on the same lifecycle hook.

Driving non-happy paths

EditorMock auto-simulate covers the happy path only: Eligible after Initialize, then Linked + OnLinkCompleted and a first progress snapshot after linking starts, then three mock notifications and a new snapshot when FetchNotifications is called. To exercise the rest of the surface - NotAvailable / Blocked / Disabled statuses, every AlmediaErrorCode, empty or oversized notification batches, native log forwarding - reach for AlmediaEditorMock. It lets host editor code and play-mode tests drive the SDK into any state in one line, so UI branches keyed off those signals become Editor-testable without a device build. The public surface lives in AlmediaSDK.Editor.Testing and consists of these static methods:
  • EmitStatus(AlmediaStatus) - delivers a status as if native had reported it; Almedia.Status and OnStatusChanged reflect it immediately. Build the status with its constructor: EmitStatus(new AlmediaStatus.Linked(false, false)) is a linked player with nothing to open, and EmitStatus(new AlmediaStatus.NotAvailable("holdout")) is a holdout player.
  • EmitError(AlmediaErrorCode, string) - delivers an error; OnErrorOccurred fires with the matching code and message.
  • EmitLinkCompleted() - fires OnLinkCompleted with the current UTC timestamp.
  • EmitNotifications(params AlmediaNotification[]) - fires OnNotificationsReceived. Pass no arguments for an empty batch; pass many to test scrolling / paging.
  • EmitInGameRewardGrant(string id, params AlmediaInGameReward[]) - fires OnInGameRewardGrantRequested. The id is optional (an overload generates one); pass the same id twice to reproduce an at-least-once redelivery. A call with no rewards is dropped by the SDK as malformed.
  • EmitProgress(AlmediaProgress) - applies a snapshot. Progress holds it and OnProgressUpdated fires. null models the native SDK clearing the snapshot. A null Username models a snapshot that clears the previous username.
  • EmitTaskCompleted(string id, AlmediaCompletedTask) / EmitBalanceChanged(string id, AlmediaRewardPoints balance, AlmediaRewardPoints change) - fire the two progress events. The id is optional. Pass the same id twice to reproduce a replay.
  • EmitScreenPresented(AlmediaScreen) - fires OnScreenPresented for the given screen, without a real webview.
  • EmitScreenDismissed(AlmediaScreen, AlmediaInAppScreenResult) - fires OnScreenDismissed for the given screen and result.
  • EmitNativeLog(AlmediaLogLevel, string) - delivers a forwarded log line through the same path the iOS/Android plugins use.
  • CancelPending() - stops any pending auto-simulate coroutine started before manual mode flipped.
Manual mode. The mock has two modes: auto-simulate (default; canned coroutines drive the happy path) and manual (every Initialize, linking start and FetchNotifications becomes a no-op so the test owns the scenario). The first call to any AlmediaEditorMock method flips manual mode for the rest of the play session and cancels any in-flight canned coroutine, so an EmitStatus(new AlmediaStatus.Blocked()) issued right after Almedia.Initialize cannot be clobbered by the delayed Eligible. Manual mode resets on domain reload (entering Play mode, recompiling) - there is no public toggle to leave it manually. Pre-init contract. Calling any Emit* method before Almedia.Initialize(...) throws InvalidOperationException. The throw is deliberate: it surfaces test ordering bugs at test-author time instead of swallowing them as a silent no-op. Stripping. AlmediaEditorMock lives in the AlmediaLink.Editor assembly definition with includePlatforms:["Editor"]. It is not compiled for iOS or Android player targets at the assembly level - host code that references it must be wrapped in #if UNITY_EDITOR, and the references will fail to compile on a player target rather than crash at runtime. A minimal play-mode test that verifies UI hides on Blocked:
The package README has a shorter pointer to the same surface; the API reference covers each method in the Editor testing section.