dotnet/maui · error · Exception

No test assembly found.

Error message

No test assembly found.

What it means

Thrown by GetTestCategories when reflection-loading a DeviceTests DLL (Controls or Core) fails for every candidate path found via GetFiles. The loop at line 204 swallows each Assembly.LoadFrom failure into a Warning, so this throw fires only when zero assemblies loaded successfully — meaning the build that should have produced Microsoft.Maui.Controls.DeviceTests.dll or Microsoft.Maui.Core.DeviceTests.dll never ran, or its output landed outside the searched ../../** glob.

Source

Thrown at eng/devices/devices-shared.cake:220

    System.Reflection.Assembly loadedAssembly = null;

    foreach (var filePath in dllFilePath)
    {
        try
        {
            loadedAssembly = System.Reflection.Assembly.LoadFrom(filePath.FullPath);
            Information($"Loaded assembly from {filePath}: {loadedAssembly.FullName}");
            break; // Exit the loop if the assembly is loaded successfully
        }
        catch (Exception ex)
        {
            Warning($"Failed to load assembly from {filePath}: {ex.Message}");
        }
    }

    if (loadedAssembly == null)
    {
        throw new Exception("No test assembly found.");
    }
	var testCategoryType = loadedAssembly.GetType("Microsoft.Maui.DeviceTests.TestCategory");

	var values = new List<string>();

	foreach (var field in testCategoryType.GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static))
	{
		if (field.FieldType == typeof(string))
		{
			values.Add($"Category={(string)field.GetValue(null)}");
		}
	}
	
	return values.ToList();
}

void HandleTestResults(string resultsDir, bool testsFailed, bool needsNameFix, string suffix = null)
{

View on GitHub (pinned to f377ff1c5e)

Solutions

  1. Run the build target first (dotnet cake build.cake --target=build) so the DeviceTests DLL exists under artifacts/bin before invoking GetTestCategories.
  2. Check the Warning lines immediately before the throw — they list each attempted path and the LoadFrom exception, which identifies whether the issue is a missing file or a load failure.
  3. Confirm projectPath ends in Controls.DeviceTests.csproj or Core.DeviceTests.csproj; other project names return early with an empty list and never reach this code, so a wrong path means the glob at line 196/200 returns nothing.
  4. If the DLL exists but LoadFrom throws BadImageFormatException, verify the build TFM/architecture matches the host process (e.g. a net*-ios DLL cannot be LoadFrom'd on a Windows host).

Example fix

// before: glob may miss the output when the build used a non-default configuration
dllFilePath = GetFiles($"{directoryPath}/../../**/Microsoft.Maui.Controls.DeviceTests.dll").ToList();

// after: fall back to the known arcade bin layout and log when empty
dllFilePath = GetFiles($"{directoryPath}/../../**/Microsoft.Maui.Controls.DeviceTests.dll").ToList();
if (dllFilePath == null || dllFilePath.Count == 0)
{
    var arcade = MakeAbsolute(new DirectoryPath($"../../artifacts/bin/Controls.DeviceTests/{configuration}"));
    dllFilePath = GetFiles($"{arcade.FullPath}/**/Microsoft.Maui.Controls.DeviceTests.dll").ToList();
}
if (dllFilePath.Count == 0)
    throw new Exception($"No test assembly found under {directoryPath}/../../** ; build the project first.");
Defensive patterns

Strategy: validation

Validate before calling

// Before calling GetTestCategories, confirm the DLL exists for the project
var expectedDll = projectPath.EndsWith("Controls.DeviceTests.csproj")
    ? "Microsoft.Maui.Controls.DeviceTests.dll"
    : "Microsoft.Maui.Core.DeviceTests.dll";
var found = GetFiles($"{Context.GetCallerInfo().SourceFilePath.GetDirectory()}/../../**/{expectedDll}");
if (found == null || !found.Any())
    throw new Exception($"Build the {expectedDll} project before requesting test categories.");

Prevention

When it happens

Trigger: Calling GetTestCategories with a Controls.DeviceTests.csproj or Core.DeviceTests.csproj path before the test project has been built; building into a configuration/TFM whose bin folder the ../../** glob does not reach; a stale or deleted artifacts/bin tree after a clean; the DLL existing but being unloadable (locked, wrong architecture, corrupt) so every LoadFrom throws.

Common situations: Running the 'test' target without first running 'build'; switching DotnetVersion/target framework so the DLL is emitted under a new folder not matched by the glob; a clean/git-clean wiping artifacts/bin; running locally on Windows where the DLL is locked by a prior test runner process; CI caching an old artifacts dir.

Related errors


AI-assisted analysis of dotnet/maui@f377ff1c5e (2026-08-13). Data as JSON: /api/errors/c6302fb6fc0f986b. Report an issue: GitHub.