UnitTestBarAndStandards.md
May 20, 2020 ยท View on GitHub
Bar
- All new non-UI code must have unit tests.
- Unit tests need to accompany the code in the PR. This provides implicit documentation of the expected behavior, and ensures that the behavior is being satisfied. In an exceptional circumstance, a PR might be approved without its unit tests, but that must not become the normal expectation.
- All unit tests should be passing locally before the PR is created.
- Unit tests need to conform to the standards listed below.
Standards
- The test project is appropriately named (tests for the
Foo.Bar.dllare inFoo.BarTests.dll) - The test namespace is appropriately named (the top level namespace matches the assembly name, then the hierarchy in the assembly is preserved. So if
Foo.Bar.dllcontains a namespace namedWidget, the tests will be in theFoo.BarTests.Widgetnamespace) - The test class is appropriately named (tests for the
ExplodingWidgetclass will be in theExplodingWidgetTestsclass) - Each test follows the naming conventions documented at Unit Test Basics Summarizing:
- Name is in the general form
MethodName_TestCase_ExpectedBehavior - If the test is expected to throw an
Exception, the test should declare this via theExpectedExceptionattribute. If the test needs to check the contents of theException, the best pattern is to catch the expectedExceptiontype, validate it, then re-throw theException
- Name is in the general form
- Each test uses appropriate
Assertmethods to validate its conditions--use the version with a hint where it seems helpful - Each test specifies a reasonable
Timeoutvalue (default to 1 second each). The test output is much more readable if the test times out and fails than if the entire build loop hangs and reaches the 60 minute time constraint- One downside to the
Timeoutattribute--since theTimeoutapplies while debugging the unit test, it basically needs to be commented out to make debugging feasible.
- One downside to the
- Appropriate mocks decouple the test class from the underlying dependencies (Dependency Injection is a common pattern for this). This is required for new classes, best effort for changes to existing classes
- If Moq is used, mocks should ideally use
MockBehavior.Strict--this isn't always possible, but using it makes for a much stronger test - If Moq is used, each Mock's
VerifyAllshould be called at the end of the test, to ensure that all configured methods were exercised as expected. Where bothMockBehavior.StrictandVerifyAll, the resulting test will exactly pin down the interaction with dependency objects