Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*.suo
*.user
*.sln.docstates
.ionide/

# Build results

Expand All @@ -16,8 +17,12 @@ build/
[Oo]bj/
Gen/

# FAKE
.fake
# Cake
tools/**
!tools/packages.config

# Visual Studio 2015 cache/options directory
.vs/

# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
!packages/*/build/
Expand Down
84 changes: 38 additions & 46 deletions Doc/generating-mocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@

## Overview

**PCLMock** includes support for generating mock implementations from interfaces in your code base. Generated code can be either C# or Visual Basic. You have two code generation options:
**PCLMock** includes support for generating mock implementations from interfaces in your code base. The core of this code generation logic is contained in the `PCLMock.CodeGeneration` project, but you will typically use that library via a code generator "front-end". There are two code generator front-ends that ship with PCLMock:

1. A T4-based approach, available via the `PCLMock.CodeGeneration.T4` package.
2. A console-based approach, available via the `PCLMock.CodeGeneration.Console` package.
1. A [C# Source Generator](https://devblogs.microsoft.com/dotnet/introducing-c-source-generators/) (currently in preview, intended to ship with C# 9).
2. A [.NET Core tool](https://docs.microsoft.com/en-us/dotnet/core/tools/global-tools), available via the `PCLMock.CodeGeneration.Console` package.

The T4-based approach integrates into Visual Studio (Xamarin Studio is not currently supported) whereas the console-based approach is a solution-level tool that you can execute how you see fit. In both cases, these tools are driven by an XML configuration file.
In both cases, these tools are driven by an XML configuration file.

## Configuration File
## XML Configuration File

In order to know which interfaces should have mocks generated, and how those mocks should be named, the **PCLMock** code generators rely on an XML configuration file. When you install the T4-based code generator, you get a default configuration file called *Mocks.xml*.
In order to know which interfaces should have mocks generated, and how those mocks should be named, the **PCLMock** code generators rely on an XML configuration file.

### Specifying Namespace Names

Expand Down Expand Up @@ -75,7 +75,7 @@ Each filter must be either an `Include` or `Exclude` element. In either case, th
<Pattern>.*, MyAssembly</Pattern>
</Include>
</Interfaces>
```
```

A more complicated filter arrangement might look like this:

Expand Down Expand Up @@ -107,35 +107,47 @@ You can read all about plugins [here](plugins.md). To configure plugins, you spe

You can include any number of plugins and they will be executed in the order you specify. This is important if more than one plugin might produce specifications for the same member. You should identify which plugin's specifications should take precedence and list it after the other plugins.

## T4-based Generation

If you're using the T4-based generation approach, you will want to add the `PCLMock.CodeGeneration.T4` package to the project in which your mocks should reside. This is probably your unit test project.
## Generator Front-ends

Once added, you will see two new files in the root of your project:
### Source Generator

* *Mocks.tt*
* *Mocks.xml*
To use the source generator front-end, follow these steps:

*Mocks.xml* is the configuration file discussed above. *Mocks.tt* is the text template that instigates generation of the mocks. If you add a new interface or modify your configuration file, you should right-click *Mocks.tt* and select **Run Custom Tool** in order to re-generate mocks.
1. Use [.NET 5 preview](https://dotnet.microsoft.com/download/dotnet/5.0) and C# 9 preview.
2. Include all relevant PCLMock projects in your solution: `PCLMock`, `PCLMock.CodeGeneration`, and `PCLMock.CodeGeneration.SourceGenerator`. NOTE: this step is only required while source generators are in early preview because the .NET team have not yet provided a mechanism to consume source generators from NuGet packages. Longer term, you won't have to bundle the code.
3. Add a configuration file to your project called `PCLMock.xml` and include it as an additional file:
```xml
<ItemGroup>
<AdditionalFiles Include="PCLMock.xml" />
</ItemGroup>
```
4. Include the source generator (this step will simplify once source generators can be consumed from a NuGet):
```xml
<ItemGroup>
<Analyzer Include="$(OutDir)..\..\..\..\PCLMock.CodeGeneration. SourceGenerator\bin\$(Configuration)\netstandard2.0\PCLMock.CodeGeneration. SourceGenerator.dll" />
</ItemGroup>
```

To change the language of the generated code, open *Mocks.tt* and modify the `language` variable as specified in the comments.
One limitation of using source generators (besides the fact that they're in preview) is that source generators can only augment code in the compiling project. This means that generated mocks _must_ reside in the same project as the code being mocked, rather than being incorporated into a separate project (usually a unit tests project). One mitigation would be to add a `Condition` to the `Analyzer` such that it is only included for certain builds where mocks are required, but not for builds intended for deployment.

## Console-based Generation
### .NET Core tool

The `PCLMock.CodeGeneration.Console` package can be added to any project because it is actually a solution-level package. This means no particular project will "own" this package but, rather, the solution will.
To install the PCLMock .NET Core tool globally:

Once added, you'll find an executable called *PCLMockCodeGen.exe* within your solution's *packages\PCLMock.CodeGeneration.Console.$version$\tools* directory. Execution of this tool requires these parameters:
```
dotnet tool install -g pclmock
```

* The path of the solution for which mocks are being generated
* The path of the XML configuration file
* The path of the output file which will contain the generated code
You can then execute it with:

You can also optionally force the language of the generated code, although it is inferred from the output file's extension. In addition, to can execute with a `-Verbose` flag gain insight into the decisions PCLMock is making during code generation.
```
pclmock
```

An example of executing this tool is:
See the console output for help, but an example of executing this tool is:

```
.\PCLMockCodeGen.exe "Path\To\MySolution.sln" "Path\To\Mocks.xml" "output.cs" -Verbose
pclmock "Path\To\MySolution.sln" "Path\To\PCLMock.xml" "output.cs" -Verbose
```

## Supplementing Generated Code
Expand Down Expand Up @@ -170,7 +182,7 @@ namespace Foo.Bar.Mocks

## Supported Members

The code generator supports everything that **PCLMock** itself supports:
The code generator (regardless of which front-end is used) supports everything that **PCLMock** itself supports:

* `get`-only properties
* `set`-only properties
Expand All @@ -183,24 +195,4 @@ The code generator supports everything that **PCLMock** itself supports:
* generic interfaces (including type constraints)
* generic methods (including type constraints)

If the code generator comes across something the **PCLMock** [doesn't inherently support](defining-mocks.md#limitations), it will just ignore it. You can then supplement the generated partial class so that the mock successfully builds.

A caveat to this is that duplicate members will be ignored, even if each is supported individually. For example:

```C#
public interface IFirst
{
void SomeMethod();
}

public interface ISecond
{
void SomeMethod();
}

public interface IThird : IFirst, ISecond
{
}
```

Here, the generated mock for `IThird` will _not_ include a `SomeMethod` implementation. This is because doing so would require either a single implementation, or multiple with one or more implemented explicitly. Either of these two options might result in a mock that is less useful to you than you would like, so the member is simply ignored. This forces you to provide the implementation yourself via a partial class, but affords you the flexibility to choose what that implementation looks like.
If the code generator comes across something the **PCLMock** [doesn't inherently support](defining-mocks.md#limitations), it will just ignore it. You can then supplement the generated partial class so that the mock successfully builds.
6 changes: 3 additions & 3 deletions Doc/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@

PCLMock's code generator includes support for plugins. These are implementations of `PCLMock.CodeGeneration.IPlugin`. Plugins are able to participate in the code generation process, generating specifications for the target mock.

PCLMock itself comes with several plugins, discussed below. They are enabled by default, but you can always disable them or replace them with your own.
PCLMock itself comes with several plugins, discussed below. Plugins are not enabled by default (apart from an internal `Default` plugin, which is always enabled), so you'll need to ensure you add them to your configuration.

## The `IPlugin` Interface

Plugins must implement the `PCLMock.CodeGeneration.IPlugin` interface. If you're writing your own plugin, you can get this interface by adding the `PCLMock.CodeGeneration` NuGet package.

The `IPlugin` interface defines members that allow you to generate code that applies to all mock instances, or only loose mocks. In either case, you need to return an instance of `Microsoft.CodeAnalysis.SyntaxNode` containing the code you wish to inject into the mock.
The `IPlugin` interface defines members that allow you to generate default values for specific types. Plugins are applied recursively, such that separate plugins can collaborate to provide a default value for complex generic types, such as `Task<List<string>>`.

## Built-in Plugins

Expand Down Expand Up @@ -140,4 +140,4 @@ This plugin can be configured thusly:

Its purpose is to ensure that any method or property returning `IDisposable` will return `System.Reactive.Disposables.Disposable.Empty` by default. This ensures that consuming code can dispose of the returned value without triggering a `NullReferenceException`.

Naturally, if the target code does not include a reference to _System.Reactive.Core.dll_ then no specification will be generated.
Naturally, if the target code does not include a reference to _System.Reactive.Core.dll_ then no specification will be generated.
13 changes: 3 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,12 @@
[![Build status](https://ci.appveyor.com/api/projects/status/wj9tyg3m99jogmqw?svg=true)](https://ci.appveyor.com/project/kentcb/pclmock)

## What?

**PCLMock** is a simple mocking framework in a Portable Class Library. The PCL targets a wide range of platforms including:

* .NET 4.5
* Windows 8
* Windows Store
* Windows Phone 8
* Xamarin iOS
* Xamarin Android
**PCLMock** is a lightweight, but powerful mocking framework targeting .NET Standard 1.0 (it was originally a Portable Class Library, hence the name).

## Why?

Existing mocking frameworks, such as [Moq](https://github.com/Moq/moq4), do not work on platforms running a limited subset of the .NET framework. Writing mocks without the aid of a framework is laborious, error-prone, and results in inconsistent code. **PCLMock** aims to reduce this pain.
At the time of inception, existing mocking frameworks (such as [Moq](https://github.com/Moq/moq4)) rely heavily on reflection and other mechanisms that are not available on more limited .NET runtimes, such as Mono. Writing mocks without the aid of a framework is laborious, error-prone, and results in inconsistent code. **PCLMock** aims to fill this gap.

## Where?

Expand Down Expand Up @@ -47,7 +40,7 @@ public void some_test()
var sut = new Foo(mockService);

// some test code here

mockService
.Verify(x => x.Login("me", "123456"))
.WasNotCalled();
Expand Down
14 changes: 0 additions & 14 deletions Src/AssemblyInfoCommon.cs

This file was deleted.

30 changes: 30 additions & 0 deletions Src/Directory.Build.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<Project>

<PropertyGroup>
<Authors>Kent Boogaart</Authors>
<Company />
<PackageLicense>https://github.com/kentcb/PCLMock/blob/master/LICENSE</PackageLicense>
<PackageProjectUrl>https://github.com/kentcb/PCLMock</PackageProjectUrl>
<PackageIcon>Logo64x64.png</PackageIcon>
<RepositoryUrl>https://github.com/kentcb/PCLMock</RepositoryUrl>
<PackageTags>.NET mono portable pcl mocking ios android xamarin</PackageTags>
<Description>PCLMock is mocking framework in a PCL, making it usable on platforms such as iOS and Android.</Description>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>

<ItemGroup>
<PackageReference Update="Buildalyzer" Version="2.6.0" />
<PackageReference Update="Buildalyzer.Workspaces" Version="2.6.0" />
<PackageReference Update="Microsoft.CodeAnalysis.Analyzers" Version="3.0.0" />
<PackageReference Update="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.6.0-4.final" />
<PackageReference Update="Microsoft.CodeAnalysis.VisualBasic.Workspaces" Version="3.6.0-4.final" />
<PackageReference Update="Microsoft.CodeAnalysis.Common" Version="3.6.0-4.final" />
<PackageReference Update="PowerArgs" Version="3.6.0" />
<PackageReference Update="System.Collections.Immutable" Version="1.7.1" />
<PackageReference Update="System.Reactive" Version="4.4.1" />
<PackageReference Update="System.Xml.XPath.XDocument" Version="4.3.0" />
<PackageReference Update="xunit" Version="2.4.1" />
<PackageReference Update="xunit.runner.visualstudio" Version="2.4.1" />
</ItemGroup>

</Project>
19 changes: 0 additions & 19 deletions Src/PCLMock.CodeGeneration.Console.nuspec

This file was deleted.

38 changes: 0 additions & 38 deletions Src/PCLMock.CodeGeneration.Console/App.config

This file was deleted.

53 changes: 0 additions & 53 deletions Src/PCLMock.CodeGeneration.Console/ConsoleLogSink.cs

This file was deleted.

Loading