Agent skill

Pacx Unit Test Writer

by neronotte in neronotte/Greg.Xrm.Command

Guide for writing local unit tests for PACX commands without relying on external services.

MITAuto-check passedTesting & QA

Install Pacx Unit Test Writer

skills CLI
$ npx skills add neronotte/Greg.Xrm.Command --skill pacx-unit-test-writer -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install neronotte/Greg.Xrm.Command pacx-unit-test-writer --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/neronotte/Greg.Xrm.Command.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.github/skills/pacx-unit-test-writer .claude/skills/pacx-unit-test-writer && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
pacx-unit-test-writer
GitHub stars
151
Token cost
~3.6k tokens
SKILL.md length
461 words
Files
1
Skills in repo
2
Repo updated
First seen
Licence
MIT

At a glance

Guide for writing local unit tests for PACX commands without relying on external services.

  • Add unit tests for a PACX command class
  • SKILL.md covers Key test infrastructure, File 1 — CommandTest.cs…, File 2 —… and Testing…, plus 2 more sections
  • Calls dotnet
  • Tasks that involve Unit testing

What it does

Pacx Unit Test Writer is an agent skill from neronotte/Greg.Xrm.Command. Guide for writing local unit tests for PACX commands without relying on external services. Use this skill when asked to write, generate, or add unit tests for a PACX command class or executor. Tests produced by this skill are fully local — they never connect to Dataverse or any other external service.

Its SKILL.md is about 3.6k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Testing & QA, covering Unit testing. The repository describes itself as: Command Line utility for Dataverse. The licence is MIT.

When your agent uses it

  • Add unit tests for a PACX command class
  • Tasks that involve Unit testing

Example prompts

  • “/pacx-unit-test-writer”

What it can do on your machine

Read from SKILL.md and the folder at commit 236875d. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • dotnet

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Pacx Unit Test Writer loads about 3.6k tokens when it runs. Until then it costs about 81 tokens; SKILL.md has 461 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~81
When it runs · the whole SKILL.md, loaded when a task matches
~3.6k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from neronotte/Greg.Xrm.Command at commit 236875d, republished under its MIT licence (© neronotte). 461 words, ~3,618 tokens.

Download SKILL.mdSave it as .claude/skills/pacx-unit-test-writer/SKILL.md (or your agent's skills folder).
name
pacx-unit-test-writer
description
Guide for writing local unit tests for PACX commands without relying on external services. Use this skill when asked to write, generate, or add unit tests for a PACX command class or executor. Tests produced by this skill are fully local — they never connect to Dataverse or any other external service.

PACX Unit Test Writer

All tests live in Greg.Xrm.Command.Core.TestSuite\Commands\<Domain>\ and mirror the source layout under Greg.Xrm.Command.Core\Commands\<Domain>\.

Every command needs two test files:

FileTests
<Verb>CommandTest.csCLI argument parsing — no DI, no Dataverse
<Verb>CommandExecutorTest.csExecutor logic — all dependencies mocked

GlobalUsings.cs already imports Microsoft.VisualStudio.TestTools.UnitTesting, Greg.Xrm.Command.Services.Output, and Moq. Do not add redundant using statements for those.


Key test infrastructure

Utility.TestParseCommand<T>(params string[] args)

Parses a CLI argument array into a typed command instance. Use this for all parser tests.

csharp
var command = Utility.TestParseCommand<DeleteCommand>("table", "delete", "--name", "my_table");
OutputToMemory

A concrete, in-memory IOutput implementation. Captures everything written to it.

  • Use when you need to assert on output text (e.g., verify an error message was printed).
  • output.ToString() returns the full captured text.
  • Color-tagged output is serialised as <Green>Done</Green> — you can search for substrings.
csharp
var output = new OutputToMemory();
// ... run executor ...
StringAssert.Contains(output.ToString(), "Done");
Mock<IOutput>

Use instead of OutputToMemory when you need to verify specific IOutput calls with Moq's Verify.

csharp
var mockOutput = new Mock<IOutput>();
mockOutput.Setup(o => o.Write(It.IsAny<object>())).Returns(mockOutput.Object);
mockOutput.Setup(o => o.WriteLine(It.IsAny<object>(), It.IsAny<ConsoleColor>())).Returns(mockOutput.Object);
// ...
mockOutput.Verify(o => o.WriteLine(
    It.Is<string>(s => s.Contains("Table not found")),
    It.IsAny<ConsoleColor>()), Times.Once);

Rule of thumb: prefer OutputToMemory for most tests; switch to Mock<IOutput> only when you need to assert that a specific message was (or was not) written.


File 1 — <Verb>CommandTest.cs (parser tests)

Test every [Option] with both its long name and its short name. Also assert that default values and null optionals are set correctly when options are omitted.

csharp
namespace Greg.Xrm.Command.Commands.<Domain>
{
    [TestClass]
    public class <Verb>CommandTest
    {
        // ── Long-name parsing ──────────────────────────────────────────────────

        [TestMethod]
        public void ParseWithLongNameShouldWork()
        {
            var command = Utility.TestParseCommand<<Verb>Command>(
                "<noun>", "<verb>",
                "--name", "TestValue");

            Assert.AreEqual("TestValue", command.Name);
        }

        // ── Short-name parsing ─────────────────────────────────────────────────

        [TestMethod]
        public void ParseWithShortNameShouldWork()
        {
            var command = Utility.TestParseCommand<<Verb>Command>(
                "<noun>", "<verb>",
                "-n", "TestValue");

            Assert.AreEqual("TestValue", command.Name);
        }

        // ── Default values ─────────────────────────────────────────────────────
        // When options with DefaultValue are omitted, assert the expected default.
        // When optional options are omitted, assert null.

        [TestMethod]
        public void DefaultValuesShouldBeSetCorrectly()
        {
            var command = Utility.TestParseCommand<<Verb>Command>(
                "<noun>", "<verb>",
                "--name", "TestValue");

            Assert.AreEqual(ExpectedDefaultForSomeOption, command.SomeOption);
            Assert.IsNull(command.OptionalOption);
            Assert.IsNull(command.SolutionName);
        }

        // ── Enum options ───────────────────────────────────────────────────────

        [TestMethod]
        public void EnumOptionShouldBeParsedCorrectly()
        {
            var command = Utility.TestParseCommand<<Verb>Command>(
                "<noun>", "<verb>",
                "--name", "TestValue",
                "--ownership", "Organization");

            Assert.AreEqual(OwnershipTypes.OrgOwned, command.Ownership);
        }

        // ── Boolean flags ──────────────────────────────────────────────────────

        [TestMethod]
        public void BoolFlagShouldDefaultToFalse()
        {
            var command = Utility.TestParseCommand<<Verb>Command>("<noun>", "<verb>", "--name", "v");
            Assert.IsFalse(command.SomeFlag);
        }

        [TestMethod]
        public void BoolFlagShouldBeParsedWhenProvided()
        {
            var command = Utility.TestParseCommand<<Verb>Command>(
                "<noun>", "<verb>",
                "--name", "v",
                "--someFlag");

            Assert.IsTrue(command.SomeFlag);
        }

        // ── Parameterised (use [DataRow] for multiple input variants) ──────────

        [TestMethod]
        [DataRow("--name",      "v1")]
        [DataRow("--otherName", "v2")]
        public void AllNameAliasesShouldMapToSameProperty(string optionKey, string optionValue)
        {
            var command = Utility.TestParseCommand<<Verb>Command>("<noun>", "<verb>", optionKey, optionValue);
            Assert.IsNotNull(command.Name);
        }
    }
}

File 2 — <Verb>CommandExecutorTest.cs (executor unit tests)

Boilerplate — standard Dataverse mock setup
csharp
using Greg.Xrm.Command.Services.Connection;
using Microsoft.PowerPlatform.Dataverse.Client;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Messages;

namespace Greg.Xrm.Command.Commands.<Domain>
{
    [TestClass]
    public class <Verb>CommandExecutorTest
    {
        // Helper: builds mocks wired together the same way as the real DI chain.
        private static (
            OutputToMemory output,
            Mock<IOrganizationServiceRepository> repoMock,
            Mock<IOrganizationServiceAsync2> crmMock)
        CreateMocks()
        {
            var output = new OutputToMemory();
            var crmMock = new Mock<IOrganizationServiceAsync2>();
            var repoMock = new Mock<IOrganizationServiceRepository>();

            repoMock
                .Setup(r => r.GetCurrentConnectionAsync())
                .ReturnsAsync(crmMock.Object);

            return (output, repoMock, crmMock);
        }
    }
}
Happy-path test
csharp
[TestMethod]
public async Task ExecuteAsync_ShouldSucceed_WhenInputIsValid()
{
    // Arrange
    var (output, repoMock, crmMock) = CreateMocks();
    OrganizationRequest? capturedRequest = null;

    crmMock
        .Setup(c => c.ExecuteAsync(It.IsAny<OrganizationRequest>()))
        .Callback<OrganizationRequest>(r => capturedRequest = r)
        .ReturnsAsync(new <ExpectedResponseType>()); // e.g. DeleteEntityResponse

    var executor = new <Verb>CommandExecutor(output, repoMock.Object);

    // Act
    var result = await executor.ExecuteAsync(
        new <Verb>Command { Name = "some_value" },
        CancellationToken.None);

    // Assert — result
    Assert.IsTrue(result.IsSuccess, result.ErrorMessage);

    // Assert — Dataverse was called
    repoMock.Verify(r => r.GetCurrentConnectionAsync(), Times.Once);
    crmMock.Verify(c => c.ExecuteAsync(It.IsAny<<ExpectedRequestType>>()), Times.Once);

    // Assert — request content
    Assert.IsNotNull(capturedRequest);
    var typedRequest = capturedRequest as <ExpectedRequestType>;
    Assert.IsNotNull(typedRequest);
    Assert.AreEqual("some_value", typedRequest./* relevant property */);
}
Failure — Dataverse returns an error
csharp
[TestMethod]
public async Task ExecuteAsync_ShouldFail_WhenDataverseThrows()
{
    // Arrange
    var (output, repoMock, crmMock) = CreateMocks();

    crmMock
        .Setup(c => c.ExecuteAsync(It.IsAny<OrganizationRequest>()))
        .ThrowsAsync(new System.ServiceModel.FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>(
            new Microsoft.Xrm.Sdk.OrganizationServiceFault(),
            "Simulated Dataverse fault"));

    var executor = new <Verb>CommandExecutor(output, repoMock.Object);

    // Act
    var result = await executor.ExecuteAsync(
        new <Verb>Command { Name = "some_value" },
        CancellationToken.None);

    // Assert
    Assert.IsFalse(result.IsSuccess);
    Assert.IsFalse(string.IsNullOrWhiteSpace(result.ErrorMessage));
}
Failure — prerequisite check fails (e.g. solution not found, entity not found)
csharp
[TestMethod]
public async Task ExecuteAsync_ShouldFail_WhenSolutionDoesNotExist()
{
    // Arrange
    var (output, repoMock, crmMock) = CreateMocks();

    // Simulate empty result set for the pre-flight query
    crmMock
        .Setup(c => c.RetrieveMultipleAsync(It.IsAny<Microsoft.Xrm.Sdk.Query.QueryBase>()))
        .ReturnsAsync(new Microsoft.Xrm.Sdk.EntityCollection()); // empty

    var executor = new <Verb>CommandExecutor(output, repoMock.Object);

    // Act
    var result = await executor.ExecuteAsync(
        new <Verb>Command { Name = "v", SolutionName = "nonexistent" },
        CancellationToken.None);

    // Assert
    Assert.IsFalse(result.IsSuccess);
    // The main operation must NOT have been called
    crmMock.Verify(c => c.ExecuteAsync(It.IsAny<OrganizationRequest>()), Times.Never);
}
Failure — no default solution set
csharp
[TestMethod]
public async Task ExecuteAsync_ShouldFail_WhenNoSolutionProvidedAndNoDefault()
{
    // Arrange
    var (output, repoMock, crmMock) = CreateMocks();

    repoMock
        .Setup(r => r.GetCurrentDefaultSolutionAsync())
        .ReturnsAsync((string?)null);

    var executor = new <Verb>CommandExecutor(output, repoMock.Object);

    // Act
    var result = await executor.ExecuteAsync(
        new <Verb>Command { Name = "some_value" /* SolutionName intentionally omitted */ },
        CancellationToken.None);

    // Assert
    Assert.IsFalse(result.IsSuccess);
    crmMock.Verify(c => c.ExecuteAsync(It.IsAny<OrganizationRequest>()), Times.Never);
}
Mocking additional injected services

When the executor depends on more services beyond IOrganizationServiceRepository, mock and inject them the same way:

csharp
var mockPluralization = new Mock<IPluralizationFactory>();
mockPluralization
    .Setup(p => p.CreateFor(It.IsAny<int>()))
    .Returns(Mock.Of<IPluralizationStrategy>(s =>
        s.GetPluralForAsync(It.IsAny<string>()) == Task.FromResult("Values")));

var executor = new <Verb>CommandExecutor(output, repoMock.Object, mockPluralization.Object);
Parameterised executor tests with [DataRow]

Use [DataRow] when several input combinations should all produce the same outcome:

csharp
[TestMethod]
[DataRow("value_a", true)]
[DataRow("value_b", true)]
[DataRow(null,      false)]
public async Task ExecuteAsync_ShouldReturnExpectedResult(string? inputName, bool expectSuccess)
{
    var (output, repoMock, crmMock) = CreateMocks();
    crmMock
        .Setup(c => c.ExecuteAsync(It.IsAny<OrganizationRequest>()))
        .ReturnsAsync(new OrganizationResponse());

    var executor = new <Verb>CommandExecutor(output, repoMock.Object);

    var result = await executor.ExecuteAsync(
        new <Verb>Command { Name = inputName },
        CancellationToken.None);

    Assert.AreEqual(expectSuccess, result.IsSuccess);
}

Testing IValidatableObject.Validate() directly

When a command implements IValidatableObject, test Validate() in isolation — no parsing, no executor.

csharp
[TestMethod]
public void Validate_ShouldFail_WhenMutuallyExclusiveOptionsAreProvided()
{
    var command = new <Verb>Command
    {
        OptionA = "value",
        OptionB = "also-value" // these two cannot coexist
    };

    var context = new System.ComponentModel.DataAnnotations.ValidationContext(command);
    var results = command.Validate(context).ToList();

    Assert.AreEqual(1, results.Count);
    CollectionAssert.Contains(results[0].MemberNames.ToList(), nameof(<Verb>Command.OptionA));
}

[TestMethod]
public void Validate_ShouldPass_WhenOnlyOneOptionIsProvided()
{
    var command = new <Verb>Command { OptionA = "value" };
    var context = new System.ComponentModel.DataAnnotations.ValidationContext(command);
    var results = command.Validate(context).ToList();
    Assert.AreEqual(0, results.Count);
}

Show full SKILL.md (181 more words)Show less

What NOT to do

❌ Avoid✅ Do instead
new OrganizationServiceRepository(...) in a unit testnew Mock<IOrganizationServiceRepository>()
[TestCategory("Integration")] on a local testOmit the category entirely
task.Wait()await executor.ExecuteAsync(...)
Asserting only Assert.IsNotNull(output.ToString())Assert on result.IsSuccess, mock .Verify(), and request content
Real file I/O or environment variablesMock the service that reads them
new OutputToConsole()new OutputToMemory() or new Mock<IOutput>()

Checklist before finishing

  • <Verb>CommandTest.cs covers every [Option] with long name and short name
  • Default values and null optionals are asserted when options are omitted
  • Boolean flags tested both absent (default false) and present (true)
  • [DataRow] used for options that have multiple meaningful input variants
  • <Verb>CommandExecutorTest.cs has a happy-path test
  • Happy-path asserts result.IsSuccess, verifies Dataverse was called, and inspects the captured request
  • At least one failure test covers a Dataverse fault (FaultException<OrganizationServiceFault>)
  • Pre-flight guard failures tested (solution not found, entity not found, etc.) and assert Dataverse operation was not called
  • No-default-solution path tested if the command reads GetCurrentDefaultSolutionAsync()
  • IValidatableObject.Validate() tested directly if the command implements it
  • No test uses a real OrganizationServiceRepository or connects to Dataverse
  • No test is marked [TestCategory("Integration")]
  • dotnet test Greg.Xrm.Command.Core.TestSuite\Greg.Xrm.Command.Core.TestSuite.csproj passes

© neronotte, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in .github/skills/pacx-unit-test-writer of neronotte/Greg.Xrm.Command.

Open the folder on GitHubat commit 236875d

Compare with similar skills

Pacx Unit Test Writer next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Pacx Unit Test Writer compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Pacx Unit Test Writer this skillneronotte/Greg.Xrm.Command151—~3.6kAutomated safety check: PassMIT
TDD WorkflowhellangleZ/burn-in-cceverywhere-ralph11211 repos~2.4kAutomated safety check: PassNone
Testing OpenLogi UIAprilNEA/OpenLogi23k—~1.1kAutomated safety check: PassApache-2.0
Go Testingcxuu/golang-skills1731 repos~1.3kAutomated safety check: PassApache-2.0
Contractssamchon/nestia2.2k—~1.3kAutomated safety check: PassMIT
Cohesion Over TestabilityEpicenterHQ/epicenter4.8k—~2kAutomated safety check: PassCustom licence

Similar skills

  • TDD Workflow

    hellangleZ/burn-in-cceverywhere-ralph

    A skill your agent uses when writing new features, fixing bugs, or refactoring code.

    112 GitHub starsUsed in 11 repos~2.4k tokens
    Testing & QAAuto-check passed
  • Testing OpenLogi UI

    AprilNEA/OpenLogi

    Verifies OpenLogi's native GPUI interface with focused tests, the component gallery and a mock agent, choosing the evidence that fits each change.

    23k GitHub stars~1.1k tokensUpdated yesterday
    Testing & QAAuto-check passed
  • Go Testing

    cxuu/golang-skills

    A skill your agent uses when writing, reviewing, or improving Go test code — including table-driven tests, subtests, parallel tests, test helpers, test doubles, and assertions with cmp.Diff.

    173 GitHub starsUsed in 1 repo~1.3k tokens
    Testing & QAAuto-check passed
  • Contracts

    samchon/nestia

    Defines self-acknowledgments for production declarations and tests.

    2.2k GitHub stars~1.3k tokensUpdated 3 days ago
    Testing & QAAuto-check passed
  • Cohesion Over Testability

    EpicenterHQ/epicenter

    Collapse test-shaped production boundaries while preserving behavior and coverage.

    4.8k GitHub stars~2k tokensUpdated 2 days ago
    Testing & QAAuto-check passed
  • JS-in-HTML Testing

    liaohch3/claude-tap

    Tests JavaScript embedded in an HTML file in two layers: pytest checks of the logic ported to Python, and Playwright runs in a real browser for the DOM.

    3.3k GitHub stars~924 tokensUpdated today
    Testing & QAAuto-check passed

More from neronotte/Greg.Xrm.Command

  • Pacx Command Writer

    neronotte/Greg.Xrm.Command

    Guide for writing new PACX CLI commands (verbs) following established project conventions.

    151 GitHub stars~3k tokensUpdated 6 days ago
    Auto-check passed

Categories

Questions about Pacx Unit Test Writer

What does Pacx Unit Test Writer do?

Guide for writing local unit tests for PACX commands without relying on external services. Command. Guide for writing local unit tests for PACX commands without relying on external services.

When should I use Pacx Unit Test Writer?

Pacx Unit Test Writer fits situations like: add unit tests for a PACX command class; tasks that involve Unit testing.

How do I install Pacx Unit Test Writer in Claude Code?

Run `npx skills add neronotte/Greg.Xrm.Command --skill pacx-unit-test-writer -a claude-code`. Or copy the skill folder (.github/skills/pacx-unit-test-writer in neronotte/Greg.Xrm.Command) into .claude/skills/pacx-unit-test-writer in your project. Claude Code loads it when a task matches its description.

How do I install Pacx Unit Test Writer in Codex?

Run `npx skills add neronotte/Greg.Xrm.Command --skill pacx-unit-test-writer -a codex`. Or copy the skill folder (.github/skills/pacx-unit-test-writer in neronotte/Greg.Xrm.Command) into .agents/skills/pacx-unit-test-writer in your project. Codex loads it when a task matches its description.

Can I use Pacx Unit Test Writer in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add neronotte/Greg.Xrm.Command --skill pacx-unit-test-writer -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/pacx-unit-test-writer, .gemini/skills/pacx-unit-test-writer, .github/skills/pacx-unit-test-writer and .opencode/skills/pacx-unit-test-writer in your project.

What does Pacx Unit Test Writer need to run?

Going by SKILL.md and its folder, Pacx Unit Test Writer needs the command-line tools its instructions call (dotnet).

Does Pacx Unit Test Writer access the network?

SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.

Is Pacx Unit Test Writer safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Pacx Unit Test Writer use?

Pacx Unit Test Writer is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Pacx Unit Test Writer use?

About 3.6k tokens (SKILL.md is roughly 14k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Pacx Unit Test Writer?

Skills that share tags, products or a category with Pacx Unit Test Writer: TDD Workflow (hellangleZ/burn-in-cceverywhere-ralph, 112 stars), Testing OpenLogi UI (AprilNEA/OpenLogi, 23k stars), Go Testing (cxuu/golang-skills, 173 stars) and Contracts (samchon/nestia, 2.2k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Pacx Unit Test Writer?

neronotte (a GitHub user) maintains it in neronotte/Greg.Xrm.Command, which has 151 GitHub stars. The repository holds 2 skills in this directory. The repository was last updated on October 4, 2026.

Source: neronotte/Greg.Xrm.Command on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.