Stryker.NET Documentation

repository·master·Indexed 24 days ago

https://github.com/stryker-mutator/stryker-net

A mutation testing tool for .NET Core and .NET Framework projects that improves test suite quality by injecting bugs (mutants) into source code via the Roslyn syntax tree. Supports .NET Core 1.1+, .NET Framework 4.5+, and .NET Standard 1.3+. Includes guides on installation via the dotnet-stryker tool, configuration using JSON or YAML, mutation levels (Basic, Standard, Advanced, Complete), and instructions for implementing custom mutators using the IMutator interface.

Tokens
25.7K
Snippets
52
Records
127
Agent score
79%

What's inside Stryker.NET

  1. What is Stryker.NET and its supported platforms

    master

    Stryker.NET is a mutation testing framework designed to broaden the mutation testing landscape for .NET projects. Its primary goals are to provide broad support for various .NET ecosystems, maintain high performance (blazing fast execution), and provide detailed reporting for bug tracking.

    Supported Platforms and Languages:

    • Frameworks: .NET Framework 4.6+, .NET Core 1.0+, and .NET 5+
    • Languages: C# and F#
  2. Understand Stryker's C# Mutation Orchestration

    master

    Stryker uses Node Orchestrators to determine how different C# syntax structures are mutated. The orchestration process follows a specific order: Stryker iterates through a list of orchestrators and executes the first one where CanHandle returns true.

    Key behaviors to note:

    • Declaration Order Matters: Orchestrators are tried in declaration order. More specific orchestrators must be declared before more general ones to ensure they are matched first.
    • Debugging Mutations: If a syntax construct is not being mutated as expected, check if a DoNotMutateOrchestrator is intercepting it or if the correct orchestrator is declared in the right order.
    • Orchestrator List: You can inspect the full list and the relative declaration order by looking at CSharpMutantOrchestrator.BuildOrchestratorList().

    To see which orchestrators are active, refer to the CSharpMutantOrchestrator class, which serves as the entry point for mutating C# source files.

  3. Maintainer responsibilities and requirements

    master

    A maintainer (synonymous with a GitHub 'Collaborator') is expected to contribute regularly and help define the scope and direction of Stryker.NET.

    Key Requirements:

    • Embody Code of Conduct values: Maintainers must model the project's values in all public interactions.
    • Security: Maintainers must have 2FA enabled on their GitHub account.
    • Decision Making: Participate in defining project scope, urgency, and boundaries.

    Maintainer Rights (at discretion):

    • Merging pull requests.
    • Modifying issues (closing, labeling, assigning).
    • Interacting with CI servers (canceling builds, restarting jobs).
    • Performing CRUD operations on GitHub integrations.
    • Participating in the decision-making process.

    Note on 'Owners': Some maintainers have administrative rights to the stryker-mutator organization and NuGet publishing access. Those with publishing access must use NuGet's 2FA.

  4. How Stryker.NET performs mutations using mutant schemata

    master

    Stryker.NET uses a technique called mutant schemata (also known as mutation switching) to achieve high performance while maintaining accuracy. Instead of modifying source code or byte code directly, the framework injects conditional logic (if statements) into the code. This allows all mutations to be compiled into a single assembly at once, enabling fast test runs and the ability to keep the mutated assembly in memory.

    Key characteristics of mutant schemata:

    • Speed: All mutants are compiled together, making the process faster than source code mutation.
    • Accuracy: It can show the exact location of mutations to the user.
    • Capabilities: Supports testing multiple mutations in a single test run and makes mutation coverage calculation easier.
    • Limitations: It cannot mutate constant values, method names, or access modifiers. Additionally, all injected mutations must be syntactically correct to avoid compile errors during the single compilation phase.
    if(Environment.GetEnvironmentVariable("ActiveMutation") == "1") {
      i--; // mutated code
    } else {
      i++; // original code
    }
  5. How xUnit behaves in Stryker.NET

    master

    xUnit has specific behaviors that affect how Stryker performs mutation testing:

    Conflicting Test Cases

    If multiple xUnit test cases share the same Guid (identifier), xUnit will report results for all of them under that single VsTest test case.

    Ignored Tests

    Tests marked as ignored in xUnit are completely ignored by the runner and are not reported to Stryker.

    Theories (Parameterized Tests)

    • Static Theories (InlineData): These are processed as distinct test cases unless they share identical parameters.
    • Run-time Theories: These may be discovered as a single test. During discovery, xUnit attempts to map data sets to test cases. If it cannot create unique display names, it may provide multiple results for the same test case.

    Execution Sequence and Risks

    1. Discovery: xUnit discovers all tests at startup. If a specific list is provided, it filters them. Note that requested tests not found during discovery will not be reported.
    2. Execution: xUnit runs all data sets for a theory, then reports results.

    Critical Risks for Stryker:

    • Coverage Spillage: Because Stryker captures coverage on testcase end, coverage from one test might be incorrectly associated with the next test if it occurs between the end and start events.
    • Isolation Issues: If a mutation changes a test's name (e.g., via ToString()), the identifier changes. This prevents the test from being run in isolation via Stryker, as Stryker can no longer predict the new name.
    TestSession start event
    xUnit discovers test
       xUnit discovers theories
         xUnit enumerates data source (for tests)
    ...
    TestCase start event
       xUnit runs test with first set of data
       xUnit runs test with second set of data
       ...
       xUnit runs test with last set of data
    
       xUnit reports first test results
    TestCase end event
       xUnit reports second test result
       ...
       xUnit reports last test result
    TestCase start event
       ...
    TestSession end event
  6. Attributes of a high-quality mutator

    master

    The Stryker project team evaluates new mutators based on these criteria:

    • Error-like: Mutations should resemble possible human mistakes rather than just maximizing quantity.
    • Performance: Mutators must be fast, as they are called recursively on every syntax element.
    • Buildability: Mutations should result in compilable code in most scenarios.
    • Survivability: Avoid mutations that almost always raise exceptions (e.g., changing array[i] to array[-i]), as these are trivial to kill and add little value.
    • Killability: Mutations must not be semantically equivalent to the original code; they must change behavior so that a test can actually detect the change.
    • Generality: Mutators should work across various projects and not be limited to niche or rarely used constructions.
  7. How Stryker.NET performs C# mutation

    master

    Stryker.NET uses the Roslyn compiler platform to parse C# source code into a syntax tree. Mutation is achieved by traversing this tree and replacing specific SyntaxNode objects with mutated versions.

    Key components of the mutation process include:

    • Mutators: Implement specific mutation strategies (e.g., changing a comparison operator).
    • SyntaxNode Orchestrators: Walk the syntax tree to find valid locations for mutations.
    • MutationContext: Tracks the state of the mutation process, including disabled mutators and the MutationStore.
    • CSharpNodeOrchestrator: The main entry point that coordinates orchestrators, mutators, and the MutantPlacer.
    • MutantPlacer: Handles the actual injection of mutations into the syntax tree and manages rollback logic.
  8. Key constraints for designing Stryker.NET mutators

    master

    When designing or improving a mutator, keep these technical constraints in mind:

    • Syntax Tree focus: Mutators must operate on the Roslyn syntax tree (object representations of code) and must not use text transformation logic.
    • Recursive Visiting: Stryker visits every syntax element. For example, a method invocation is visited as a whole, then its object, then its name, then its parameters (recursively).
    • File Isolation: Each file is mutated separately; mutators cannot exploit information from multiple files simultaneously.
    • Error Handling: Stryker handles rollbacks for compilation errors, so mutators don't need to guarantee perfect compilation. However, avoid triggering ambiguous errors that might cause Stryker to roll back many valid mutations in safe mode.
  9. F# mutation process and the Program.fs flag requirement

    master

    Due to limitations in the Compile function regarding the lastcompiled flag, the mutation process requires specific handling. The lastcompiled flag must be set on the last file in the compilation sequence to prevent compiler failure.

    Currently, this flag is hardcoded onto Program.fs because Program.fs is treated as the last file in the project during testing. Because this flag must be set on the ParsedInput, the orchestrator operates using FSharpList<SynModuleOrNamespace> instead of ParsedInput to facilitate this flag setting before the final compilation step.

  10. Test only changed code using `since`

    master

    The since feature uses git information to test only the code changes since a specific target (committish). Stryker will only report on mutants within the changed code; all other mutants will not have a result.

    Configuration Options:

    • since.enabled: Enables or disables the feature. If the since object exists in the config, it is assumed enabled unless this is explicitly set to false.
    • since.target: The git target to compare against (e.g., master, develop, or a branch name). Defaults to master.
    • since.ignore-changes-in: An array of file patterns (using globbing syntax) to ignore if they appear in the diff (e.g., ['**/*Translations.json']).

    Usage:

    • CLI: --since:<committish> (e.g., --since:feat-2)
    • Config: "since": { "target": "feat-2" }
  11. Convert collections and handle Async using Microsoft.FSharp

    master

    Stryker.net utilizes Microsoft.FSharp to bridge the gap between C# and F# types and execution models:

    Collection Conversion

    Use Microsoft.FSharp.Collections to convert between standard C# lists and F# lists. This is necessary because functions in FSharp.Compiler.Service expect FSharpList.

    • FSharpList
    • ListModule.OfSeq (used to convert C# List to FSharpList and vice versa)

    Async Execution

    Because many functions in FSharp.Compiler.Service are asynchronous, you must use Microsoft.FSharp.Control.FSharpAsync.RunSynchronously to ensure the process is correctly identified as originating from F#.