A unit test generation extension for Visual Studio that aims to always produce code that compiles - covering the basic cases automatically and preparing as much as it can for the complex cases.
Get the extension from the Visual Studio Marketplace:
Alternatively see the releases on GitHub:
You can configure Unitverse in three ways:
.unitTestGeneratorConfig
file which works just like a .editorConfig
file.It is worth noting that these options apply in slightly different scopes.
.unitTestGeneratorConfig
file apply to all solutions at the same level or a child level on diskYou can create a .unitTestGeneratorConfig
file which contains the options you currently have configured in Visual Studio by going to the ‘Export’ options page. For more information on this, see the ‘Setting options per-project’ section below.
The per-generation user interace is, by default, set to show only when a target project can not be automatically matched. You can change this by going to Tools->Options->Unitverse or by setting the relevant option in a .unitTestGeneratorConfig
file, or by holding the Control key while selecting any ‘Generate tests…’ menu item. This user interface also allows you to override the target project selection, if you want to generate tests in a different project or because your naming format isn’t consistent. To see what the user interface looks like, please see the ‘Per-generation user interface’ section below.
The options are also layered - configuration files and session settings are not a complete set of settings - just what is configured in the file or dialog. The layers are loaded in order:
If you are not sure how a particular option has been configured, you can open up the per-generation UI, which has a small icon next to each option which you can hover to find out where a configuration value came from. Also, options that are detected from a target project that differ from the options loaded from Visual Studio / config files will cause a message to be logged to the Output window in Visual Studio.
The source icons are as follows:
The default for project naming is {0}.Tests
. For example, a project named MyProject
would be associated with a test project called MyProject.Tests
.
You can specify multiple patterns in the ‘Project naming convention’ option by separating them with a semicolon. So for example, if the naming pattern was set to {0}.Tests;{0}.UnitTests;{0}.Cheese
then it would first look for MyProject.Tests
, then MyProject.UnitTests
and finally MyProject.Cheese
.
You can override the default naming behaviour by going to Tools->Options->Unitverse->Project Mappings and entering the source and target project. Alternatively, you can set these in a .unitTestGeneratorConfig
file with a [Mappings]
section:
[Mappings]
SourceProject=TargetProject
OtherSourceProject=OtherTargetProject
In this scenario, tests for types in the SourceProject project would be created in the TargetProject project. Note that when adding mappings in .unitTestGeneratorConfig
files, the changes are additive. So any extra mappings defined in configuration files defined closer to the project will add to the list of mappings already defined, not replace them.
The default for the class and file naming is {0}Tests
. For example, a class called MyClass
would be associated with a test class called MyClassTests
and a file called a class called MyClass.cs
would be associated with a test class called MyClassTests.cs
. Note that Unitverse can be configured to find test types that match the correct type name even if they are in an incorrectly named file. By default it will do this only for a class in the correct namespace. However, you can configure this to match any class in the target project, or disable the fallback finding mechanism by going to Tools -> Options -> Unitverse -> Generation Options -> Fallback type finding method.
Unitverse generates tests using ‘strategies’. There are strategies for testing constructors & parameters, properties, methods & parameters etc. Not all tests are useful for every code base. One example would be that null checks are less useful in a codebase making use of nullable reference types. In this instance, you may want to turn off ‘Constructors - Parameter Checks’ and ‘Methods - Parameter Checks’. The groups that you can disable are listed in the table below. All groups are enabled by default.
Category | Group | Meaning |
---|---|---|
Constructors | Basic Checks | Whether to emit basic constructor checks (CanConstruct) |
Constructors | Parameter Checks | Whether to emit null and string parameter checks for constructors (CannotConstructWith*) |
Initializers | Basic Checks | Whether to emit basic constructor checks (CanInitialize) |
Initializers | Parameter Checks | Whether to emit null and string parameter checks for initializers (CannotInitializeWith*) |
Methods | Basic Checks | Whether to emit tests that exercise methods (CanCall*) |
Methods | Mapping Method Checks | Whether to emit tests that check mapping methods (*PerformsMapping) |
Methods | Parameter Checks | Whether to emit null and string parameter checks for methods (CannotCallWith*) |
Indexers | Basic Checks | Whether to emit tests to exercise indexers (CanGet/Set*) |
Properties | Basic Checks | Whether to emit tests to exercise properties (CanGet/Set*) |
Properties | Initialized Property Checks | Whether to emit tests that check that properties have been initialized based on constructor parameter names (*IsInitializedCorrectly) |
Operators | Basic Checks | Whether to emit tests to exercise operators (CanCallOperator) |
Operators | Parameter Checks | Whether to emit null and string parameter checks for operators (CannotCallOperatorWith*) |
Interfaces | Implementation Checks | Whether to emit checks that verify interface implementation (Implements*) |
Unitverse allows you to customise the method names that are generated, so you can fit the extension in with your existing code base.
There are replacable tokens which can be substituted into the method names as follows:
Token | Meaning | Available |
---|---|---|
typeName | The name of the type that the tested item belongs to | Always |
interfaceName | The name of the interface for which generation is currently being performed | When generating tests for an interface implementation |
memberName | The unambiguous name of the member being tested (see below) | Everywhere except constructors |
memberBareName | The ambiguous name of the member being tested (see below) | Everywhere except constructors |
parameterName | The name of the parameter being tested for guard conditions | Method guard condition generation & dependency field name generation |
typeParameters | The list of type parameters for an interface | When generating tests for an interface implementation |
There are also formatting options for token replacement. This can be achieved with the {token:format}
syntax - so for instance {typeName:lower}
will produce the name of the type in lower case. The formatters available are as follows:
Suffix | Meaning |
---|---|
lower | The token value is emitted in lower case |
upper | The token value is emitted in upper case |
camel | The token value is emitted in camel case (i.e. the first letter is converted to lower case) |
pascal | The token value is emitted in pascal case (i.e. the first letter is converted to upper case) |
When generating for a method with overloads, the names are decorated with ‘WithParameterName’ suffixes so that each method can be clearly identified. The unambiguous name includes these suffixes and the ambiguous name does not.
You can set settings per-project if you need to (for example if you work with some code that uses MSTest and some that uses NUnit). In order to do this, you can create a .unitTestGeneratorConfig
file, which is formatted similarly to .editorConfig files, just ignoring the file type heading.
You can set any member of the IGenerationOptions, IStrategyOptions or the INamingOptions interfaces using this method. For example, the following content in a .unitTestGeneratorConfig
would set the test framework to MSTest, the mocking framework to NSubstitute and the test project naming convention to <project_name>.UnitTests
:
test_project_naming={0}.UnitTests
framework-type=MSTest
MockingFrameworkType=NSubstitute
To generate a .unitTestGeneratorConfig
file that contains all the options as they are set in the Visual Studio Options dialogue, you can go to the ‘Options Export’ configuration page which contains the functionality allowing you to export the current configuration.
Note that the formatting for the member names is case insensitive, and underscores and hyphens are ignored. Hence frameworkType
, framework-type
, FRAMEWORK_TYPE
and FRAME-WORK-TYPE
all resolve to the FrameworkType
member of IGenerationOptions
. The rules for file finding & resolution work exactly the same as they do for .editorConfig
files - in short, all folders from the project file to the root are searched for a .unitTestGeneratorConfig
file, and files ‘closer’ to the project file take precedence if the same option is set in more than one file.
You can also use a UI to modify a .unitTestGeneratorConfig
file by double-clicking on the file in the solution explorer. When loading a file, it may not contain all the configurable elements. However, after saving it will contain all the configuration options, similar to when you export the configuration from the Visual Studio Options dialog.
If you are using the per-generation user interface (either because you have configured it in Tools->Options->Unitverse or because you are holding Control while selecting a ‘Generate tests…’ menu item), then a dialog is shown before each generation. The dialog is organised into four tabs as shown in the below images. At the bottom is a drop-down labeled ‘Apply to:’ - this allows you to save the options that you have selected. The options are:
.unitTestGeneratorConfig
. Note that this will overwrite any existing file.Setting the target project in which tests will be generated:
Setting the options relating to generation. Note that most of the options you can configure are here, but some (for example ‘project naming’) do not make sense in this context and are not shown:
Setting the options relating to which strategies to apply:
Setting the options relating to naming: