Skip to content

Commit

Permalink
Plugging in Assertion extensibility (#117)
Browse files Browse the repository at this point in the history
* Plugged in Assertion extensibility as defined by https://github.com/Microsoft/testfx-docs/blob/master/RFCs/002-Framework-Extensibility-Custom-Assertions.md.

* Added an E2E test as well.

* Fixing a doc typo.
  • Loading branch information
AbhitejJohn authored Mar 20, 2017
1 parent 787b06c commit 9192d83
Show file tree
Hide file tree
Showing 18 changed files with 608 additions and 16 deletions.
61 changes: 60 additions & 1 deletion TestFx.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26217.2
VisualStudioVersion = 15.0.26228.9
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{FF8B1B72-55A1-4FFE-809E-7B79323ED8D0}"
EndProject
Expand Down Expand Up @@ -100,6 +100,9 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlatformServices.Desktop.Component.Tests", "test\ComponentTests\PlatformServices.Desktop.Component.Tests\PlatformServices.Desktop.Component.Tests.csproj", "{4EA6EED3-0579-4C7C-94EA-52B2AD2A9D39}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Smoke.E2E.Tests", "test\E2ETests\Smoke.E2E.Tests\Smoke.E2E.Tests.csproj", "{A94E3FCA-2FB3-4C79-9F39-5B652B6992F0}"
ProjectSection(ProjectDependencies) = postProject
{4004757A-0080-4410-B90A-6169B20F151B} = {4004757A-0080-4410-B90A-6169B20F151B}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Automation.CLI", "test\E2ETests\Automation.CLI\Automation.CLI.csproj", "{9C1219E0-E775-47F9-9236-63F03F774801}"
ProjectSection(ProjectDependencies) = postProject
Expand Down Expand Up @@ -151,6 +154,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scripts", "scripts", "{BCF5
scripts\UpdateTemplates.ps1 = scripts\UpdateTemplates.ps1
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{92F8E9A2-903E-4025-99BC-7DC478D5466D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FxExtensibility", "samples\FxExtensibility\FxExtensibility.csproj", "{A82770C0-1FF5-43C7-8790-471D5E4F8D6E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FxExtensibilityTestProject", "test\E2ETests\TestAssets\FxExtensibilityTestProject\FxExtensibilityTestProject.csproj", "{4004757A-0080-4410-B90A-6169B20F151B}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\TestFramework\Extension.Shared\Extension.Shared.projitems*{272ca5e1-8e81-4825-9e47-86cce02f700d}*SharedItemsImports = 13
Expand Down Expand Up @@ -754,6 +763,54 @@ Global
{0A4A76DD-FEE1-4D04-926B-38E1A24A7ED2}.Release|x64.Build.0 = Release|Any CPU
{0A4A76DD-FEE1-4D04-926B-38E1A24A7ED2}.Release|x86.ActiveCfg = Release|Any CPU
{0A4A76DD-FEE1-4D04-926B-38E1A24A7ED2}.Release|x86.Build.0 = Release|Any CPU
{A82770C0-1FF5-43C7-8790-471D5E4F8D6E}.Code Analysis Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A82770C0-1FF5-43C7-8790-471D5E4F8D6E}.Code Analysis Debug|Any CPU.Build.0 = Debug|Any CPU
{A82770C0-1FF5-43C7-8790-471D5E4F8D6E}.Code Analysis Debug|ARM.ActiveCfg = Debug|Any CPU
{A82770C0-1FF5-43C7-8790-471D5E4F8D6E}.Code Analysis Debug|ARM.Build.0 = Debug|Any CPU
{A82770C0-1FF5-43C7-8790-471D5E4F8D6E}.Code Analysis Debug|x64.ActiveCfg = Debug|Any CPU
{A82770C0-1FF5-43C7-8790-471D5E4F8D6E}.Code Analysis Debug|x64.Build.0 = Debug|Any CPU
{A82770C0-1FF5-43C7-8790-471D5E4F8D6E}.Code Analysis Debug|x86.ActiveCfg = Debug|Any CPU
{A82770C0-1FF5-43C7-8790-471D5E4F8D6E}.Code Analysis Debug|x86.Build.0 = Debug|Any CPU
{A82770C0-1FF5-43C7-8790-471D5E4F8D6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A82770C0-1FF5-43C7-8790-471D5E4F8D6E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A82770C0-1FF5-43C7-8790-471D5E4F8D6E}.Debug|ARM.ActiveCfg = Debug|Any CPU
{A82770C0-1FF5-43C7-8790-471D5E4F8D6E}.Debug|ARM.Build.0 = Debug|Any CPU
{A82770C0-1FF5-43C7-8790-471D5E4F8D6E}.Debug|x64.ActiveCfg = Debug|Any CPU
{A82770C0-1FF5-43C7-8790-471D5E4F8D6E}.Debug|x64.Build.0 = Debug|Any CPU
{A82770C0-1FF5-43C7-8790-471D5E4F8D6E}.Debug|x86.ActiveCfg = Debug|Any CPU
{A82770C0-1FF5-43C7-8790-471D5E4F8D6E}.Debug|x86.Build.0 = Debug|Any CPU
{A82770C0-1FF5-43C7-8790-471D5E4F8D6E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A82770C0-1FF5-43C7-8790-471D5E4F8D6E}.Release|Any CPU.Build.0 = Release|Any CPU
{A82770C0-1FF5-43C7-8790-471D5E4F8D6E}.Release|ARM.ActiveCfg = Release|Any CPU
{A82770C0-1FF5-43C7-8790-471D5E4F8D6E}.Release|ARM.Build.0 = Release|Any CPU
{A82770C0-1FF5-43C7-8790-471D5E4F8D6E}.Release|x64.ActiveCfg = Release|Any CPU
{A82770C0-1FF5-43C7-8790-471D5E4F8D6E}.Release|x64.Build.0 = Release|Any CPU
{A82770C0-1FF5-43C7-8790-471D5E4F8D6E}.Release|x86.ActiveCfg = Release|Any CPU
{A82770C0-1FF5-43C7-8790-471D5E4F8D6E}.Release|x86.Build.0 = Release|Any CPU
{4004757A-0080-4410-B90A-6169B20F151B}.Code Analysis Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4004757A-0080-4410-B90A-6169B20F151B}.Code Analysis Debug|Any CPU.Build.0 = Debug|Any CPU
{4004757A-0080-4410-B90A-6169B20F151B}.Code Analysis Debug|ARM.ActiveCfg = Debug|Any CPU
{4004757A-0080-4410-B90A-6169B20F151B}.Code Analysis Debug|ARM.Build.0 = Debug|Any CPU
{4004757A-0080-4410-B90A-6169B20F151B}.Code Analysis Debug|x64.ActiveCfg = Debug|Any CPU
{4004757A-0080-4410-B90A-6169B20F151B}.Code Analysis Debug|x64.Build.0 = Debug|Any CPU
{4004757A-0080-4410-B90A-6169B20F151B}.Code Analysis Debug|x86.ActiveCfg = Debug|Any CPU
{4004757A-0080-4410-B90A-6169B20F151B}.Code Analysis Debug|x86.Build.0 = Debug|Any CPU
{4004757A-0080-4410-B90A-6169B20F151B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4004757A-0080-4410-B90A-6169B20F151B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4004757A-0080-4410-B90A-6169B20F151B}.Debug|ARM.ActiveCfg = Debug|Any CPU
{4004757A-0080-4410-B90A-6169B20F151B}.Debug|ARM.Build.0 = Debug|Any CPU
{4004757A-0080-4410-B90A-6169B20F151B}.Debug|x64.ActiveCfg = Debug|Any CPU
{4004757A-0080-4410-B90A-6169B20F151B}.Debug|x64.Build.0 = Debug|Any CPU
{4004757A-0080-4410-B90A-6169B20F151B}.Debug|x86.ActiveCfg = Debug|Any CPU
{4004757A-0080-4410-B90A-6169B20F151B}.Debug|x86.Build.0 = Debug|Any CPU
{4004757A-0080-4410-B90A-6169B20F151B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4004757A-0080-4410-B90A-6169B20F151B}.Release|Any CPU.Build.0 = Release|Any CPU
{4004757A-0080-4410-B90A-6169B20F151B}.Release|ARM.ActiveCfg = Release|Any CPU
{4004757A-0080-4410-B90A-6169B20F151B}.Release|ARM.Build.0 = Release|Any CPU
{4004757A-0080-4410-B90A-6169B20F151B}.Release|x64.ActiveCfg = Release|Any CPU
{4004757A-0080-4410-B90A-6169B20F151B}.Release|x64.Build.0 = Release|Any CPU
{4004757A-0080-4410-B90A-6169B20F151B}.Release|x86.ActiveCfg = Release|Any CPU
{4004757A-0080-4410-B90A-6169B20F151B}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -804,5 +861,7 @@ Global
{FE0DF239-0D81-46CA-9277-3342009E83F9} = {929A3EDE-893B-4801-82BA-01FD947291CB}
{F60B647A-E14C-48AF-9BD6-89F44EB60115} = {929A3EDE-893B-4801-82BA-01FD947291CB}
{BCF525B1-E67F-486D-B091-06A8BB8A2793} = {929A3EDE-893B-4801-82BA-01FD947291CB}
{A82770C0-1FF5-43C7-8790-471D5E4F8D6E} = {92F8E9A2-903E-4025-99BC-7DC478D5466D}
{4004757A-0080-4410-B90A-6169B20F151B} = {D53BD452-F69F-4FB3-8B98-386EDA28A4C8}
EndGlobalSection
EndGlobal
49 changes: 49 additions & 0 deletions samples/FxExtensibility/AssertEx.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace MSTest.Extensibility.Samples
{
using Microsoft.VisualStudio.TestTools.UnitTesting;

public static class AssertEx
{
private static AssertIs assertis;

/// <summary>
/// A simple assert extension to validate if an object is of a given type.
/// </summary>
/// <typeparam name="T">The type to check.</typeparam>
/// <param name="assert">Assert class.</param>
/// <param name="obj">The object.</param>
/// <returns>True if object is of the given type.</returns>
/// <exception cref="AssertFailedException">If object is not of the given type.</exception>
public static bool IsOfType<T>(this Assert assert, object obj)
{
if (obj is T)
{
return true;
}

throw new AssertFailedException(
string.Format(
"Expected object of type {0} but found object of type {1}",
typeof(T),
obj ?? obj.GetType()));
}

/// <summary>
/// A chain/grouping of assert statements.
/// </summary>
/// <param name="assert">The Assert class.</param>
/// <returns>The class that contains the assert methods for this grouping.</returns>
public static AssertIs Is(this Assert assert)
{
if (assertis == null)
{
assertis = new AssertIs();
}

return assertis;
}
}
}
46 changes: 46 additions & 0 deletions samples/FxExtensibility/AssertIs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace MSTest.Extensibility.Samples
{
using Microsoft.VisualStudio.TestTools.UnitTesting;

/// <summary>
/// A grouping of Assert statements with similar functionality.
/// </summary>
public class AssertIs
{
/// <summary>
/// Determines if the 'divisor' is actually a divisor of 'number'.
/// </summary>
/// <param name="number">A number.</param>
/// <param name="divisor">Its proclaimed divisor.</param>
/// <returns>True if it is a divisor</returns>
/// <exception cref="AssertFailedException">If it is not a divisor.</exception>
public bool Divisor(int number, int divisor)
{
if (number % divisor == 0)
{
return true;
}

throw new AssertFailedException(string.Format("{0} is not a divisor of {1}", divisor, number));
}

/// <summary>
/// Determines if a number is positive.
/// </summary>
/// <param name="number">The number.</param>
/// <returns>True if it is positive.</returns>
/// <exception cref="AssertFailedException">If the number is not positive.</exception>
public bool Positive(int number)
{
if (number > 0)
{
return true;
}

throw new AssertFailedException(string.Format("{0} is not positive", number));
}
}
}
48 changes: 48 additions & 0 deletions samples/FxExtensibility/FxExtensibility.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<TestFxRoot Condition="$(TestFxRoot) == ''">..\..\</TestFxRoot>
</PropertyGroup>
<Import Project="$(TestFxRoot)scripts\build\TestFx.Settings.targets" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{A82770C0-1FF5-43C7-8790-471D5E4F8D6E}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MSTest.Extensibility.Samples</RootNamespace>
<AssemblyName>MSTest.Extensibility.Samples</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<Compile Include="AssertEx.cs" />
<Compile Include="AssertIs.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\TestFramework\MSTest.Core\MSTest.Core.csproj">
<Project>{7252d9e3-267d-442c-96bc-c73aef3241d6}</Project>
<Name>MSTest.Core</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(TestFxRoot)scripts\build\TestFx.targets" />
</Project>
36 changes: 36 additions & 0 deletions samples/FxExtensibility/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Reflection;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("MSTest.Extensibility.Samples")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("FxExtensibility")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("a82770c0-1ff5-43c7-8790-471d5e4f8d6e")]

// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
32 changes: 29 additions & 3 deletions src/TestFramework/MSTest.Core/Assertions/Assert.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,46 @@
namespace Microsoft.VisualStudio.TestTools.UnitTesting
{
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

/// <summary>
/// A collection of helper classes to test various conditions within
/// unit tests. If the condition being tested is not met, an exception
/// is thrown.
/// </summary>
public static partial class Assert
public class Assert
{
private static Assert that;

#region Singleton constructor

/// <summary>
/// Gets the singleton instance of the Assert functionality.
/// </summary>
/// <remarks>
/// Users can use this to plug-in custom assertions through C# extension methods.
/// For instance, the signature of a custom assertion provider could be "public static void IsOfType&lt;T&gt;(this Assert assert, object obj)"
/// Users could then use a syntax similar to the default assertions which in this case is "Assert.That.IsOfType&lt;Dog&gt;(animal);"
/// More documentation is at "https://github.com/Microsoft/testfx-docs".
/// </remarks>
public static Assert That
{
get
{
if (that == null)
{
that = new Assert();
}

return that;
}
}

#endregion

#region Boolean

/// <summary>
Expand Down
30 changes: 29 additions & 1 deletion src/TestFramework/MSTest.Core/Assertions/CollectionAssert.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,36 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting
/// with collections within unit tests. If the condition being tested is not
/// met, an exception is thrown.
/// </summary>
public static class CollectionAssert
public class CollectionAssert
{
private static CollectionAssert that;

#region Singleton constructor

/// <summary>
/// Gets the singleton instance of the CollectionAssert functionality.
/// </summary>
/// <remarks>
/// Users can use this to plug-in custom assertions through C# extension methods.
/// For instance, the signature of a custom assertion provider could be "public static void AreEqualUnordered(this CollectionAssert cusomtAssert, ICollection expected, ICollection actual)"
/// Users could then use a syntax similar to the default assertions which in this case is "CollectionAssert.That.AreEqualUnordered(list1, list2);"
/// More documentation is at "https://github.com/Microsoft/testfx-docs".
/// </remarks>
public static CollectionAssert That
{
get
{
if (that == null)
{
that = new CollectionAssert();
}

return that;
}
}

#endregion

#region Membership

/// <summary>
Expand Down
Loading

0 comments on commit 9192d83

Please sign in to comment.