devlooped/moq
The most popular and friendly mocking framework for .NET
Mixed signals — read the receipts
worst of 4 axesnon-standard license (Other); no tests detected
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 1d ago
- ✓15 active contributors
- ✓Distributed ownership (top contributor 45% of recent commits)
Show 4 more →Show less
- ✓Other licensed
- ✓CI configured
- ⚠Non-standard license (Other) — review terms
- ⚠No test directory detected
What would change the summary?
- →Use as dependency Concerns → Mixed if: clarify license terms
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Forkable" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/devlooped/moq)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/devlooped/moq on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: devlooped/moq
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/devlooped/moq shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
WAIT — Mixed signals — read the receipts
- Last commit 1d ago
- 15 active contributors
- Distributed ownership (top contributor 45% of recent commits)
- Other licensed
- CI configured
- ⚠ Non-standard license (Other) — review terms
- ⚠ No test directory detected
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live devlooped/moq
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/devlooped/moq.
What it runs against: a local clone of devlooped/moq — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in devlooped/moq | Confirms the artifact applies here, not a fork |
| 2 | License is still Other | Catches relicense before you depend on it |
| 3 | Default branch main exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 31 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of devlooped/moq. If you don't
# have one yet, run these first:
#
# git clone https://github.com/devlooped/moq.git
# cd moq
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of devlooped/moq and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "devlooped/moq(\\.git)?\\b" \\
&& ok "origin remote is devlooped/moq" \\
|| miss "origin remote is not devlooped/moq (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(Other)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"Other\"" package.json 2>/dev/null) \\
&& ok "license is Other" \\
|| miss "license drift — was Other at generation time"
# 3. Default branch
git rev-parse --verify main >/dev/null 2>&1 \\
&& ok "default branch main exists" \\
|| miss "default branch main no longer exists"
# 4. Critical files exist
test -f "src/Moq/Mock.cs" \\
&& ok "src/Moq/Mock.cs" \\
|| miss "missing critical file: src/Moq/Mock.cs"
test -f "src/Moq/Language/Setup/ISetup.cs" \\
&& ok "src/Moq/Language/Setup/ISetup.cs" \\
|| miss "missing critical file: src/Moq/Language/Setup/ISetup.cs"
test -f "src/Moq/Linq/MockQueryable.cs" \\
&& ok "src/Moq/Linq/MockQueryable.cs" \\
|| miss "missing critical file: src/Moq/Linq/MockQueryable.cs"
test -f "src/Moq/Interception/MockInvocation.cs" \\
&& ok "src/Moq/Interception/MockInvocation.cs" \\
|| miss "missing critical file: src/Moq/Interception/MockInvocation.cs"
test -f "src/Moq/Matching/Match.cs" \\
&& ok "src/Moq/Matching/Match.cs" \\
|| miss "missing critical file: src/Moq/Matching/Match.cs"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 31 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~1d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/devlooped/moq"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
Moq is a mocking framework for .NET that uses LINQ expression trees and lambda expressions to enable type-safe, refactoring-friendly mock object creation without record/replay patterns. It supports mocking both interfaces and classes with a fluent API, and includes a unique "LINQ to Mocks" feature (Mock.Of<T>) that generates mocks inline from lambda expressions matching desired behavior. Core library organized as a single-solution project (Moq.sln) with the main mocking engine in the root src/ directory, test suites alongside it, and documentation/website assets in docs/. Build configuration is managed via Directory.Build.rsp (common across all projects) and .editorconfig standardizes code style. GitHub Actions workflows are centralized in .github/workflows/ with shared configuration in includes.yml and matrix definitions (os-matrix.json, dotnet.json).
👥Who it's for
.NET unit test authors (using xUnit, NUnit, MSTest, etc.) who want to mock dependencies without the ceremony of other frameworks; pragmatic developers doing state or interaction TDD who prefer a low-barrier-to-entry, expression-based API over configuration-heavy alternatives.
🌱Maturity & risk
Highly mature and production-ready. The repo shows 1.79M lines of C# code, is actively maintained with frequent commits, has comprehensive GitHub Actions CI/CD pipelines (build.yml, publish.yml), and is the de facto standard mocking library for .NET with massive adoption. Latest release tracking is evident from .github/release.yml and changelog.md maintenance.
Very low risk. Single maintainer (devlooped organization) model is mitigated by the project's ubiquity and long track record in the ecosystem. No hidden dependency management issues visible—the project carefully controls its surface. Primary risk is API breaking changes across major versions, but semantic versioning is clearly followed (version badges in README).
Active areas of work
Active maintenance indicated by multiple workflows: dotnet.yml handles CI testing across OS/framework combinations, changelog.yml auto-generates release notes, dependabot.yml manages dependency updates, and publish.yml automates NuGet releases. The .github/copilot-instructions.md suggests recent adoption of AI-assisted development guidance. Recent focus appears to be on CI robustness and cross-platform .NET support.
🚀Get running
git clone https://github.com/devlooped/moq.git && cd moq && dotnet build Moq.sln && dotnet test Moq.sln
Daily commands: dotnet build Moq.sln (to compile) and dotnet test Moq.sln (to run test suites). For local iteration: dotnet watch test in a test project directory for continuous test execution.
🗺️Map of the codebase
src/Moq/Mock.cs— Core Mock<T> class that is the primary API entry point for creating and configuring mocks; every contributor must understand the setup/verify patternsrc/Moq/Language/Setup/ISetup.cs— Defines the fluent setup interface contract that chains Setup(), Returns(), Callback() and other configuration methodssrc/Moq/Linq/MockQueryable.cs— Implements the Mock.Of<T>() LINQ-to-Mocks feature which is a key differentiator of this librarysrc/Moq/Interception/MockInvocation.cs— Handles method invocation interception and argument capture; critical to matching setups with actual callssrc/Moq/Matching/Match.cs— Core matcher system that evaluates expressions and arguments against setup predicatessrc/Moq/Language/Flow/IVerifies.cs— Defines the fluent Verify() interface for asserting mock behavior; essential verification patternMoq.sln— Solution file defining all project references and build configuration; entry point for developers setting up their environment
🛠️How to make changes
Add a new custom Matcher
- Create a new matcher class extending Matcher<T> in src/Moq/Matching/Matchers.cs (
src/Moq/Matching/Matchers.cs) - Implement Matches() and DescribeTo() methods to define matching logic and failure messages (
src/Moq/Matching/Matchers.cs) - Add a static factory method to Match class to expose your matcher (
src/Moq/Matching/Match.cs) - Add tests in src/Moq.Tests/CustomMatcherFixture.cs to validate behavior (
src/Moq.Tests/CustomMatcherFixture.cs)
Add a new fluent Setup configuration method
- Define a new interface extending ISetup in src/Moq/Language/Setup/ (
src/Moq/Language/Setup/ISetup.cs) - Implement the interface in SetupPhrase<T,TResult> class in src/Moq/Language/Setup/SetupPhrase.cs (
src/Moq/Language/Setup/SetupPhrase.cs) - Chain the new interface from existing setup methods to extend the fluent API (
src/Moq/Language/Setup/SetupPhrase.cs) - Add tests validating the new configuration option (
src/Moq.Tests/CallbacksFixture.cs)
Add support for a new language or test type
- Create a new project directory under src/ (e.g., src/Moq.Tests.NewLanguage/) (
src/Moq.Tests) - Create language-specific test files that exercise mocking patterns in that language (
src/Moq.Tests.VisualBasic/IssueReports.vb) - Add the new project to Moq.sln to integrate into the build pipeline (
Moq.sln) - Update .github/workflows/build.yml to include the new test project in CI (
.github/workflows/build.yml)
Add a custom DefaultValueProvider for special types
- Create a class implementing IDefaultValueProvider in src/Moq/DefaultValueProvider.cs or alongside it (
src/Moq/DefaultValueProvider.cs) - Override CreateDefault() to return custom defaults for your types (
src/Moq/DefaultValueProvider.cs) - Pass your provider to Mock<T> constructor via MockBehavior and DefaultValueProvider parameters (
src/Moq/Mock.cs) - Add tests in src/Moq.Tests/CustomDefaultValueProviderFixture.cs (
src/Moq.Tests/CustomDefaultValueProviderFixture.cs)
🔧Why these technologies
- Castle.DynamicProxy — Runtime dynamic proxy generation allows Moq to create mock implementations for any interface or virtual class member without compilation; avoids the record/replay pattern of older frameworks
- Expression Trees (.NET LINQ) — Enables type-safe, compile-time-checked fluent API Setup(x => x.Method()) and LINQ to Mocks; developers get IntelliSense and refactoring support
- Reflection & Method Interception — Captures actual invocations at runtime to match against setups and track call history for verification
- Multi-targeting (.NET Framework, .NET Core, .NET 5+) — Ensures Moq works across legacy and modern .NET ecosystems; critical for adoption in diverse enterprises
⚖️Trade-offs already made
-
Dynamic proxy generation vs. source generators
- Why: Dynamic proxies allow zero setup (just new Mock<T>()) and support ad-hoc types; source generation would require explicit codegen setup but could improve compile-time checking
- Consequence: Runtime overhead of proxy creation and method interception; no compile-time proof that mock behavior is valid
-
Expression tree parsing for Setup() vs. magic string names
- Why: Expression trees provide compile-time safety and refactoring support; magic strings would be brittle
- Consequence: Complexity of expression analysis and norm
🪤Traps & gotchas
No hidden environment variable requirements detected. Key gotcha: IL generation relies on runtime behavior that may differ across .NET Framework vs. .NET Core—test matrices in os-matrix.json and dotnet.json ensure coverage, but local changes may behave differently on different runtime versions. Watch for expression tree serialization edge cases when mocking complex generic scenarios. The build uses Directory.Build.rsp to inject version info—running dotnet commands outside the solution context may lose this context.
🏗️Architecture
💡Concepts to learn
- Expression Trees (System.Linq.Expressions) — Core to Moq's design—Mock.Setup() and Mock.Of<T>() both work by parsing lambda expressions into abstract syntax trees to extract method calls and property accesses for dynamic verification.
- Reflection.Emit (IL Generation) — Moq generates mock classes at runtime using IL bytecode generation; understanding this explains why mocking interfaces is faster than classes and why some edge cases fail.
- Dynamic Proxy Pattern — Moq uses Castle.Core's dynamic proxies to intercept method calls on mocked objects; this is how Setup/Verify tracking works under the hood.
- Interaction-Based Testing (Mock Verification) — Moq's Verify() and Times helpers enable interaction testing (asserting how methods were called) as opposed to state testing; this is a core design philosophy of the library.
- LINQ to Objects (Mock.Of<T>) — Moq's unique feature of inline mock generation via lambda expressions; the name 'LINQ to Mocks' reflects using LINQ syntax to declaratively define mock behavior without explicit Setup calls.
- Type-Safe Mocking via Generics — Mock<T> is a generic class where T is compile-time checked; this prevents many mocking mistakes at compile time rather than runtime, differentiating Moq from older reflection-based mocking.
🔗Related repos
NSubstitute/NSubstitute— Direct competitor mocking framework for .NET with a similar fluent API but emphasizing substitution over mocking; used when developers prefer Substitute.For<T>() over Mock<T>().FakeItEasy/FakeItEasy— Alternative .NET mocking library with a different philosophy (AAA-style) for creating fakes; represents a stylistic alternative to Moq's expression-tree approach.Xunit/Xunit— Primary test framework used by Moq itself (visible in test projects); most Moq users pair it with xUnit for test execution.castleproject/Core— Castle.Core dynamic proxy library underlying Moq's IL generation and mock class creation at runtime; Moq depends on this for interface/class interception.devlooped/sponsorable— Sibling project from the same maintainer (devlooped) using similar build/CI infrastructure, showing ecosystem maturity and cross-project patterns.
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive COM interop tests for src/Moq.Tests.ComTypes
The repo includes src/Moq.Tests.ComTypes with a ComTypes.idl file and a README.md, but there's no evidence of actual test files exercising COM interop scenarios. COM mocking is a complex edge case that deserves dedicated test coverage. This would validate that Moq correctly handles COM interfaces, COM events, and COM-specific marshaling scenarios.
- [ ] Review src/Moq.Tests.ComTypes/README.md to understand the current COM type setup
- [ ] Create unit test files in src/Moq.Tests.ComTypes/ that exercise mocking of COM interfaces (IDispatch, IUnknown-derived interfaces)
- [ ] Add tests for COM event handling and COM object marshaling scenarios
- [ ] Ensure tests run as part of the main build.yml CI pipeline
- [ ] Document any platform-specific requirements (Windows-only tests) in test attributes
Add F# integration tests in src/Moq.Tests.FSharpTypes
The repo has src/Moq.Tests.FSharpTypes/HasAbstractActionEvent.fs, indicating F# language support is a concern, but there's no visible test suite validating F# compatibility. Given Moq's cross-language .NET audience, comprehensive F# tests for abstract members, events, and type inference would prevent regressions and improve confidence in F# support.
- [ ] Create additional F# test files in src/Moq.Tests.FSharpTypes/ covering mocking of F#-specific constructs (discriminated unions interfaces, computation expressions, F# events)
- [ ] Add tests validating mock setup and verification syntax works naturally from F# code
- [ ] Ensure F# tests are included in the main test matrix (add to .github/workflows/build.yml if not present)
- [ ] Document F#-specific limitations or best practices in docs/user-guide/ (e.g., function member mocking)
Add GitHub Action workflow for NuGet package validation
The repo has publish.yml for publishing but no dedicated workflow validating the generated NuGet package contents before release. This would catch issues like missing or incorrect dependencies, missing source link metadata, or incorrect package metadata early. A new workflow in .github/workflows/ should validate package integrity.
- [ ] Create .github/workflows/nuget-validation.yml that runs on publish or PR
- [ ] Validate package metadata (version, description, license, repository URL match source)
- [ ] Verify all expected DLLs and symbols are present in the nupkg
- [ ] Validate SourceLink integration is correctly embedded (deterministic builds, source reference)
- [ ] Test package can be restored and types are discoverable in a test project
- [ ] Integrate validation into the publish.yml workflow or main build pipeline
🌿Good first issues
- Add integration test coverage for Mock.Of<T> (LINQ to Mocks) with nested generic types—the codebase has expression tree tests but LINQ-specific edge cases (e.g., Mock.Of<IDictionary<string, Task<int>>>(...)) likely lack comprehensive examples.
- Enhance documentation in docs/_includes/ with a runnable example showing the difference between Setup/Verify patterns vs. Mock.Of patterns—the README hints at LINQ to Mocks but docs/ doesn't have a dedicated comparison guide.
- Add Span<T> and Memory<T> mock support tests (check if they exist in test projects)—modern .NET performance patterns rely on these, and the changelog likely has feature requests for better support.
⭐Top contributors
Click to expand
Top contributors
- @kzu — 45 commits
- @devlooped-bot — 29 commits
- @dependabot[bot] — 12 commits
- @Asafima — 2 commits
- @rauhs — 2 commits
📝Recent commits
Click to expand
Recent commits
11ce350— ⬆️ Bump files with dotnet-file sync (kzu)5c620af— ⬆️ Bump files with dotnet-file sync (devlooped-bot)6c7b352— Prevent inclusion of OSMF for now (kzu)8316dc2— Bump NuGetizer from 1.4.6 to 1.4.7 (dependabot[bot])13ccd53— Bump NuGetizer from 1.2.4 to 1.4.6 (dependabot[bot])d31084a— Simplify dotnet install versions lookup step (kzu)2f0a155— Allow specifying required dotnet versions in a file (kzu)835cefd— Address build warnings (kzu)8c010b0— Remove unnecessary upstream file (kzu)0514353— ⬆️ Bump files with dotnet-file sync (devlooped-bot)
🔒Security observations
The Moq repository demonstrates a strong security posture overall. It is a well-established, open-source mocking framework with no critical or high-severity vulnerabilities detected based on static analysis of the visible file structure. The project uses standard GitHub Actions workflows for CI/CD, includes proper licensing, and maintains organized project structure. Minor improvements include: adding a SECURITY.md policy file, removing binary artifacts from version control, implementing a CODEOWNERS file, and ensuring explicit dependency version pinning. No hardcoded secrets, SQL injection risks, XSS vulnerabilities, or significant misconfigurations were identified in the visible structure.
- Low · Missing Dependency Lock File —
Repository root / Dependencies. No package-lock.json, packages.lock.json, or similar dependency lock file is visible in the repository structure. While this is a .NET project using NuGet, explicit lock files help prevent supply chain attacks by ensuring reproducible builds. Fix: Ensure NuGet package versions are pinned in project files (.csproj) and consider using a central packages file (Directory.Packages.props) for unified version management across the solution. - Low · No SECURITY.md or Security Policy —
Repository root. The repository does not appear to have a SECURITY.md file defining vulnerability disclosure procedures. This makes it unclear how security issues should be reported. Fix: Create a SECURITY.md file with clear guidelines for responsible vulnerability disclosure, including contact information and expected response timeframes. - Low · Potential Binary Artifact in Source Control —
src/Moq.Tests.ComTypes/Moq.Tests.ComTypes.dll. The file 'src/Moq.Tests.ComTypes/Moq.Tests.ComTypes.dll' appears to be a compiled binary checked into version control, which is generally not recommended for source repositories. Fix: Remove the .dll file from version control and either: (1) regenerate it from source during build, or (2) reference it from a package repository. Update .gitignore to exclude binary artifacts. - Low · Missing CODEOWNERS File —
Repository root / .github/. No CODEOWNERS file is present. This makes it harder to ensure security reviews are conducted by appropriate team members before merging code. Fix: Create a .github/CODEOWNERS file to define code ownership and ensure security-sensitive changes are reviewed by trusted maintainers.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.