Skip to main content

orchardcore-unit-test

Writes and runs OrchardCore tests — xUnit unit tests, SiteContext-based integration tests, Moq mocking, and Playwright functional tests. Use when the user needs to add a test, set up a test harness/tenant for tests, mock OrchardCore services, or run the test suite.

معلومات المصدر

المستودع
OrchardCMS/OrchardCore
آخر نشاط في المصدر
٢٩ يونيو ٢٠٢٦ في ١٨:٢٣
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٨٬٢٠١
التفرعات
٢٬٦٠١

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

مستكشف الملفات
2 ملفات

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
orchardcore-unit-test
description
Writes and runs OrchardCore tests — xUnit unit tests, SiteContext-based integration tests, Moq mocking, and Playwright functional tests. Use when the user needs to add a test, set up a test harness/tenant for tests, mock OrchardCore services, or run the test suite.
# OrchardCore Unit & Integration Testing This skill guides you through writing and running OrchardCore tests following project conventions. OrchardCore uses **xUnit v3** (Microsoft Testing Platform — test projects are `Exe`). Tests live under `test/`: | Project | Kind | |---------|------| | `OrchardCore.Tests` | unit + in-process integration (main) | | `OrchardCore.Abstractions.Tests` | pure unit tests for core abstractions | | `OrchardCore.Tests.Integration` | external-service integration (S3, etc.) | | `OrchardCore.Tests.Functional` | Playwright browser automation | | `OrchardCore.Benchmarks` | BenchmarkDotNet (not xUnit) | ## Decide the test type | Testing… | Type | Harness | |----------|------|---------| | Pure logic, a single class | unit | plain xUnit + Moq | | A driver/service needing DI | unit | build a small `ServiceCollection` | | Content APIs, recipes, tenant behavior | integration | `SiteContext` | | Admin/front-end through a browser | functional | `OrchardTestFixture` + Playwright | ## Workflow A: unit test ### Step 1: Add a test class Naming: `{Subject}Tests`; methods `{Action}_{Condition}_{ExpectedResult}`, for example `Write_WithinLimit_Succeeds`. ```csharp namespace OrchardCore.Json.Nodes.Test; public class Base64Tests { [Theory] [InlineData("YTw+OmE/", "a<>:a?")] [InlineData("SGVsbA==", "Hell")] public void DecodeToString_ValidBase64_ReturnsDecodedString(string source, string expected) { Assert.Equal(expected, Base64.FromUTF8Base64String(source)); } } ``` Use `[Fact]` for single cases, `[Theory]` + `[InlineData]`/`[MemberData]` for parameterized. Assertions are xUnit `Assert.*` (no Shouldly in this repo). ### Step 2: Mock dependencies with Moq ```csharp using Moq; // Quick stub: var clock = Mock.Of<IClock>(c => c.UtcNow == DateTime.UtcNow); // With setup/verify: var shellHost = new Mock<IShellHost>(); shellHost.Setup(h => h.GetScopeAsync(It.IsAny<string>())).ReturnsAsync(scope); // ... shellHost.Verify(h => h.GetScopeAsync("Default"), Times.Once); ``` Build a service provider when the unit needs DI: ```csharp var httpContext = new DefaultHttpContext { RequestServices = new ServiceCollection() .AddSingleton(myService.Object) .BuildServiceProvider(), }; ``` ## Workflow B: integration test with SiteContext `SiteContext` spins up a real tenant (SQLite by default) from a recipe and gives you an `HttpClient` + tenant scope. ```csharp public class BlogPostApiControllerTests { [Fact] public async Task CreateDraft_ExistingContentItem_CreatesDraft() { using var context = new SiteContext(); await context.InitializeAsync(); var response = await context.Client.PostAsJsonAsync("api/content?draft=true", contentItem); var draft = await response.Content.ReadAsAsync<ContentItem>(); Assert.True(draft.Latest); Assert.False(draft.Published); } } ``` Resolve tenant services inside a scope: ```csharp await context.UsingTenantScopeAsync(async scope => { var session = scope.ServiceProvider.GetRequiredService<ISession>(); var posts = await session.Query<ContentItem, ContentItemIndex>(x => x.ContentType == "BlogPost").ListAsync(); Assert.Equal(2, posts.Count()); }); ``` Customize the recipe by subclassing or `WithRecipe`: ```csharp public class AgencyContext : SiteContext { public AgencyContext() => this.WithRecipe("Agency"); } ``` Defaults: `RecipeName = "Blog"`, `DatabaseProvider = "Sqlite"`, a random tenant name + table prefix per test. ## Workflow C: functional test (Playwright) `OrchardTestFixture` starts a CMS server and a headless Chromium browser. ```csharp var page = await fixture.CreatePageAsync(); await page.GotoAsync("/"); await Expect(page.Locator("h1")).ToBeVisibleAsync(); ``` Set `PLAYWRIGHT_TRACING` to capture screenshots/snapshots/sources into `traces/`. ## Running tests ```bash # All tests in a project (from repo root) dotnet test test/OrchardCore.Tests/OrchardCore.Tests.csproj # Filter by name (xUnit / MTP) dotnet test test/OrchardCore.Tests/OrchardCore.Tests.csproj --filter "FullyQualifiedName~BlogPost" ``` CI requires all tests green. If you change CSS/JS, run `yarn build` first (asset tests). ## Quick Reference ### xUnit attributes | Attribute | Use | |-----------|-----| | `[Fact]` | one test case | | `[Theory]` + `[InlineData]` | inline parameter sets | | `[Theory]` + `[MemberData(nameof(X))]` | computed parameter sets | ### Common assertions `Assert.Equal`, `Assert.True/False`, `Assert.Null/NotNull`, `Assert.Contains`, `Assert.Throws<T>`, `await Assert.ThrowsAsync<T>(...)`. ### SiteContext members | Member | Purpose | |--------|---------| | `InitializeAsync()` | create + set up the tenant | | `Client` | `HttpClient` bound to the tenant | | `UsingTenantScopeAsync(fn)` | run code in the tenant's DI scope | | `GraphQLClient` | GraphQL API client | | `RecipeName` / `DatabaseProvider` | override before `InitializeAsync` | ### Moq cheatsheet | Need | Code | |------|------| | Stub a property | `Mock.Of<I>(x => x.P == v)` | | Setup a method | `m.Setup(x => x.F(It.IsAny<T>())).ReturnsAsync(r)` | | Verify a call | `m.Verify(x => x.F(arg), Times.Once)` | | Pass the object | `m.Object` | ## Gotchas - Test projects are `Exe` (MTP) — keep that `OutputType` when adding one; don't switch to library. - `SiteContext` is `IDisposable` — always `using var context = ...`. - Resolve tenant services only inside `UsingTenantScopeAsync`; the outer scope isn't the tenant. - Integration tests use SQLite + a fresh per-test table prefix; tests must not assume shared state. - Guard refactors with tests — the contributing guide requires new tests for refactoring. ## References - `references/testing.md` — SiteContext internals, fixtures, Playwright, project layout - `src/docs/contributing/contributing-code.md` (repo) — test expectations - `test/OrchardCore.Tests/` (repo) — real examples - `AGENTS.md` (repo root) — build commands
عرض على GitHub