|
| 1 | +# F# Testing proposal |
| 2 | + |
| 3 | +## Why do we test |
| 4 | + |
| 5 | +* To prevent regressions (behavioral, performance). |
| 6 | +* To have a quicker debug feedback (thus, find problems quicker). |
| 7 | +* To verify conformance to language spec (API contract testing). |
| 8 | +* To have IL verification (both read and write). |
| 9 | +* To have a quicker design feedback. |
| 10 | +* To document behavior. |
| 11 | + |
| 12 | +## Goals |
| 13 | + |
| 14 | +* Use one standardized testing framework across all of test projects, and get rid of custom old solutions (FSharpQA and Cambridge suites). |
| 15 | +* Have tests restructured the way, that they are easy to discover. |
| 16 | +* Have tests building and running on all supported platforms (Windows, macOS and Linux) and different frameworks (with exceptions when this is not applicable). |
| 17 | +* Make it easy to run tests using standard .NET instruments (dotnet cli, test explorer, etc.). |
| 18 | +* Leverage standard .NET testing platform and use all its benefits, suck as live unit testing, code coverage collecting, dead code elimination, etc. |
| 19 | + |
| 20 | +## Framework for testing |
| 21 | + |
| 22 | +The following test frameworks and libraries will be used for new test projects **[xUnit Test Framework](https://xunit.net/), [FluentAssertions](https://fluentassertions.com/) (+ [FsUnit](https://fsprojects.github.io/FsUnit/) and [FsCheck](https://github.com/fscheck/FsCheck) when needed)**. All existing NUnit test suites will be migrated to xUnit. |
| 23 | + |
| 24 | +**Justification:** |
| 25 | + |
| 26 | +* **xUnit** is an extensible, TDD adherent, testing framework, which was successfully adopted by many .NET engineering teams, including Roslyn, AspNetCore, EFcore, etc, has a "cleaner" approach for writing test suites (i.e. class constructor for setup, implementing IDisposable for teardown, as oppose to custom attributes). More info [here](https://xunit.net/docs/comparisons). |
| 27 | +* **FluentAssertions** makes it easier to write scoped assertions, provides better error messages. |
| 28 | + |
| 29 | +**Alternatives:** NUnit, MSBuild, Expecto |
| 30 | + |
| 31 | +### Tests categorization |
| 32 | + |
| 33 | +#### New tests should be grouped based on two factors: test type (1) + test category and subcategory (2) |
| 34 | + |
| 35 | +1. **Test type**: |
| 36 | +**Determines what type of test is it:** |
| 37 | + * __Functional tests__: |
| 38 | + * __Unit Tests__: a lightweight testing for smaller modules, functions, etc. |
| 39 | + * __Examples__: Testing individual parts/functions of lexer, parser, syntax tree, standard library modules, etc. |
| 40 | + * __Subgroups__: there should be a separation between testing private and public parts of each module (i.e. compiler tests for private and public API should be in separate test projects). |
| 41 | + * __Component Tests__: testing for bigger parts of compiler. |
| 42 | + * __Examples__: Tests for the compiler components as whole, such as Code generation, IL Generation, Compiler optimizations, Type Checker, Type Providers, Conformance, etc. |
| 43 | + * __Integration and End2End Tests__: testing of F# compiler & tooling integration, as well as e2e experiences. |
| 44 | + * __Examples__: VS Integration, .NET Interactive integration, LSP integration. Integration with dotnet CLI, project creation, building, running. |
| 45 | + * __Non-functional tests__: |
| 46 | + * __Load and Stress Tests__: testing for high level modules/components to understand peak performance and potentially catch any performance regressions. |
| 47 | + * __Examples__: measuring compile, build, link times for the compiler, individual functions (i.e. data structures sorting, traversing, etc.). |
| 48 | +1. **Test category and subcategory**: Tests (sub)categories shall be determined by the project, library, module, and functionality tests are covering. |
| 49 | + |
| 50 | +#### Examples |
| 51 | + |
| 52 | +* F# compiler component test which is verifying generated IL for computation expression will have category `Compiler` and subcategories `EmittedIL` and `ComputationExpressions`. |
| 53 | +* F# compiler service unit test which is testing F# tokenizer, will have category `Compiler.Service` and subcategory `Tokenizer`. |
| 54 | + |
| 55 | +Please, refer to [File and project structure](#File-and-project-structure) for more information on how tests will be organized on the filesystem. |
| 56 | + |
| 57 | +## File and project structure |
| 58 | + |
| 59 | +### Naming schema |
| 60 | + |
| 61 | +The proposed naming schema for test projects is: `FSharp.Category.Subcategory.TestType`, where |
| 62 | +`Category.Subcategory` is either a corresponding source project, or a more generic component (e.g. `Compiler`, `Compiler.Private` or more granular `Compiler.CodeGen`, `Compiler.CodeGen.EmittedIL` if category or subcategory project becomes too big, etc.) and `TestType` is the type of the test (one of `UnitTests`, `ComponentTests`, `IntegrationTests`, `LoadTests`). |
| 63 | + |
| 64 | +### Projects organization |
| 65 | + |
| 66 | +Please refer to the "[Naming schema](#Naming-schema)" section above for more information on the projects naming. |
| 67 | + |
| 68 | +New test projects will be grouped by category and test type, all subcategories are just test folders/files in the test project. |
| 69 | + |
| 70 | +* __Examples__: Having test project organized like: |
| 71 | + > `tests/FSharp.Compiler.ComponentTests/CodeGen/EmittedIL/BasicTests.fs` |
| 72 | + > `tests/FSharp.Compiler.ComponentTests/CodeGen/StringEncoding/StringTests.fs` |
| 73 | + > `tests/FSharp.Compiler.ComponentTests/Optimizations/Inlining/InliningTests.fs` |
| 74 | +
|
| 75 | + Will result in one test dll "`FSharp.Compiler.ComponentTests.dll`" which will contain all the subcategories of tests. |
| 76 | +* **Notes**: |
| 77 | + * This will result in reduced fragmentation of tests, all the tests files are under one big category, easier to understand what each component/unit test suite covers, less confusion in test classification for new tests. |
| 78 | + * If some categories (or subcategories) will become big enough - they can be factored out to a separate project. |
| 79 | + |
| 80 | +### Test Utilities/Helpers |
| 81 | + |
| 82 | +For all new and migrated tests, any common/helper functionality shall be factored out to a separate project - `FSharp.Test.Utilities`. |
| 83 | + |
| 84 | +## New tests |
| 85 | + |
| 86 | +* All new tests should be created in the new projects only. |
| 87 | +* All new tests should contain a brief docstring description of what is being tested, link to an issue if applicable. |
| 88 | +* All new tests should be categorized using xUnit's `Trait`, based on their `Category` and `Subcategories`. |
| 89 | + |
| 90 | +## Migrating existing tests |
| 91 | + |
| 92 | +Existing FSharpQA and Cambridge need to be migrated to corresponding test projects: component-style tests to the `FSharp.Compiler.ComponentTests` and unittest-style tests - `FSharp.Compiler.UnitTests`, `FSharp.Compiler.Private.Scripting.UnitTests`, `FSharp.Build.UnitTests`, etc. |
| 93 | + |
| 94 | +## Next steps |
| 95 | + |
| 96 | +* [**In Progress**] Move `FSharp.TestHelpers` to `FSharp.Test.Utilities`. |
| 97 | +* [**In Progress**] Create initial test projects structure for new tests (`FSharp.Compiler.ComponentTests`). |
| 98 | +* [**In Progress**] Migrate existing `NUnit` tests to xUnit. |
| 99 | +* [**In progress**] Change build scripts to run new suites as well as old ones. |
| 100 | +* Start migration of existing (namely, FSharpQA and Cambridge) suites to xUnit-based projects. |
| 101 | + |
| 102 | +## Open questions: |
| 103 | + |
| 104 | +* As far as I know, [FSharp.Compiler.Service](https://github.com/fsharp/FSharp.Compiler.Service) is dependant on some of the F# compiler tests. Does it have to be changed as well? |
| 105 | + |
| 106 | +## Other |
| 107 | + |
| 108 | +Related issues: (https://github.com/dotnet/fsharp/issues/7075) |
| 109 | + |
| 110 | +You can find this document under 'tests/README.md'. |
| 111 | + |
| 112 | +**I would like to hear some feedback the community, so we can quickly re-iterate over it (if needed), and start working :)** |
0 commit comments