MSpec中Establish、Because、It的调用机制与区别问询
Hey there! Let's unpack all your questions about Machine.Specifications (MSpec) step by step—this framework's "magic" makes total sense once you peek under the hood.
How does MSpec distinguish between Establish, Because, and It?
Even though all three look like parameterless, void-returning delegates, they're actually distinct delegate types defined by MSpec. When the test runner executes your tests, it uses reflection to scan your test class's fields and checks their type (not their variable names) to categorize them:
Establish: Marked as setup logic (runs first to initialize test state)Because: Marked as the action under test (runs after setup to trigger the behavior you're testing)It: Marked as assertion logic (runs last to verify outcomes)
This type-based categorization is how MSpec knows which code belongs to which phase of the test lifecycle.
Why can't I use It instead of Establish?
MSpec enforces a strict execution order for each test case:
- Run all
Establishdelegates (setup) - Run the
Becausedelegate (test action) - Run one
Itdelegate (assertion)
If you tried to use It for setup code, the runner would treat that logic as an assertion. That means your setup would run after the Because action (or not at all, depending on how the runner processes it), leaving your test state uninitialized and your assertions likely to fail. The separate types exist to enforce separation of concerns and ensure the correct execution flow.
How do It delegates relate to Establish and Because?
MSpec test classes represent a single test context. All fields (instance or static) in the class are shared across the setup, action, and assertion phases for a given test case. Here's the flow for each It (each It is its own independent test):
- The runner creates a new instance of your test class
- Executes all
Establishdelegates to initialize the test state (e.g., settingengineId) - Executes the
Becausedelegate to perform the action you're testing - Executes the current
Itdelegate to validate the resulting state
This ensures every assertion runs against a fresh, properly initialized state.
Who triggers these delegates?
MSpec includes a test runner (either integrated into your IDE like Visual Studio Test Explorer, or a command-line tool like mspec.exe). This runner does all the heavy lifting:
- Scans your test assembly for classes that inherit from MSpec's
Specificationbase class - Uses reflection to discover all
Establish,Because, andItfields in those classes - Executes the delegates in the correct order for each test case (each
Itcounts as a separate test)
Why is your example code not allowed?
Let's break down the issues in this snippet:
public class foobar: setup { private static int engineId; It of = () => { }; It should = () => { }; }
- Incorrect base class:
setupisn't a valid MSpec base class—you need to inherit fromMachine.Specifications.Specification(often imported with ausingdirective so you just write: Specification). Without this, the runner won't recognize your class as a test context. - Private fields: MSpec's runner uses reflection to discover your delegate fields, which means they need to be
publicorinternal(notprivate). Private fields are invisible to the runner, so it won't detect yourItdelegates as test cases. - (Minor) Naming conventions: While not a strict error, MSpec expects class names that describe the context (e.g.,
When_initializing_an_engine) andItnames that describe the expected outcome (e.g.,It should_set_a_valid_engine_id). This makes tests more readable, but it's not why the code is "not allowed."
内容的提问来源于stack exchange,提问作者Kieran

