diff --git a/src/app/FakeLib/DotCover.fs b/src/app/FakeLib/DotCover.fs
index d22bfe4a03f..5a94ae9874a 100644
--- a/src/app/FakeLib/DotCover.fs
+++ b/src/app/FakeLib/DotCover.fs
@@ -6,6 +6,7 @@ open System
open System.IO
open System.Text
open Fake.Testing.XUnit2
+open Fake.Testing.NUnit3
open Fake.MSTest
type DotCoverReportType =
@@ -194,6 +195,36 @@ let DotCoverNUnit (setDotCoverParams: DotCoverParams -> DotCoverParams) (setNUni
traceEndTask "DotCoverNUnit" details
+/// Runs the dotCover "cover" command against the NUnit test runner.
+/// ## Parameters
+///
+/// - `setDotCoverParams` - Function used to overwrite the dotCover report default parameters.
+/// - `setNUnitParams` - Function used to overwrite the NUnit default parameters.
+///
+/// ## Sample
+///
+/// !! (buildDir @@ buildMode @@ "/*.Unit.Tests.dll")
+/// |> DotCoverNUnit
+/// (fun dotCoverOptions -> { dotCoverOptions with
+/// Output = artifactsDir @@ "NUnitDotCoverSnapshot.dcvr" })
+/// (fun nUnitOptions -> { nUnitOptions with
+/// DisableShadowCopy = true })
+let DotCoverNUnit3 (setDotCoverParams: DotCoverParams -> DotCoverParams) (setNUnitParams: NUnit3Params -> NUnit3Params) (assemblies: string seq) =
+ let assemblies = assemblies |> Seq.toArray
+ let details = assemblies |> separated ", "
+ traceStartTask "DotCoverNUnit" details
+
+ let parameters = NUnit3Defaults |> setNUnitParams
+ let args = buildNUnit3Args parameters assemblies
+
+ DotCover (fun p ->
+ {p with
+ TargetExecutable = parameters.ToolPath
+ TargetArguments = args
+ } |> setDotCoverParams)
+
+ traceEndTask "DotCoverNUnit" details
+
/// Runs the dotCover "cover" command against the XUnit2 test runner.
/// ## Parameters
///
diff --git a/src/app/FakeLib/FakeLib.fsproj b/src/app/FakeLib/FakeLib.fsproj
index 62ad2572b02..22aae55fba6 100644
--- a/src/app/FakeLib/FakeLib.fsproj
+++ b/src/app/FakeLib/FakeLib.fsproj
@@ -68,6 +68,7 @@
+
diff --git a/src/app/FakeLib/UnitTest/NUnit/Common.fs b/src/app/FakeLib/UnitTest/NUnit/Common.fs
index da6eba0b277..2186d602b35 100644
--- a/src/app/FakeLib/UnitTest/NUnit/Common.fs
+++ b/src/app/FakeLib/UnitTest/NUnit/Common.fs
@@ -104,11 +104,11 @@ type NUnitParams =
/// ## Defaults
/// - `IncludeCategory` - `""`
/// - `ExcludeCategory` - `""`
-/// - `ToolPath` - `""`
+/// - `ToolPath` - The `nunit-console.exe` path if it exists in a subdirectory of the current directory.
/// - `ToolName` - `"nunit-console.exe"`
/// - `DontTestInNewThread`- `false`
/// - `StopOnError` - `false`
-/// - `OutputFile` - The `nunit-console.exe` path if it exists in a subdirectory of the current directory.
+/// - `OutputFile` - `"TestResult.xml"`
/// - `Out` - `""`
/// - `ErrorOutputFile` - `""`
/// - `WorkingDir` - `""`
diff --git a/src/app/FakeLib/UnitTest/NUnit/NUnit3.fs b/src/app/FakeLib/UnitTest/NUnit/NUnit3.fs
new file mode 100644
index 00000000000..2792db8054e
--- /dev/null
+++ b/src/app/FakeLib/UnitTest/NUnit/NUnit3.fs
@@ -0,0 +1,280 @@
+[]
+module Fake.Testing.NUnit3
+
+open System
+open System.Text
+open System.IO
+open Fake
+
+/// Process model for NUnit 3 to use.
+type NUnit3ProcessModel =
+ | DefaultProcessModel
+ | SingleProcessModel
+ | SeparateProcessModel
+ | MultipleProcessModel with
+ member x.ParamString =
+ match x with
+ | DefaultProcessModel -> ""
+ | SingleProcessModel -> "Single"
+ | SeparateProcessModel -> "Separate"
+ | MultipleProcessModel -> "Multiple"
+/// The --domain option controls of the creation of AppDomains for running tests. See [NUnit-Console Command Line Options](http://www.nunit.org/index.php?p=consoleCommandLine&r=2.6.4)
+type NUnit3DomainModel =
+ /// The default is to use multiple domains if multiple assemblies are listed on the command line. Otherwise a single domain is used.
+ | DefaultDomainModel
+ /// No domain is created - the tests are run in the primary domain. This normally requires copying the NUnit assemblies into the same directory as your tests.
+ | NoDomainModel
+ /// A test domain is created - this is how NUnit worked prior to version 2.4
+ | SingleDomainModel
+ /// A separate test domain is created for each assembly
+ | MultipleDomainModel with
+ member x.ParamString =
+ match x with
+ | DefaultDomainModel -> ""
+ | NoDomainModel -> "None"
+ | SingleDomainModel -> "Single"
+ | MultipleDomainModel -> "Multiple"
+
+/// The --framework option in running NUnit 3. There are three kinds - VXY, which means either .NET framework or Mono, NetXY (use .NET framework with given version)
+/// and MonoXY (Mono framework with given version). You can use Net or Mono to let NUnit select the version.
+/// You can pass any value using Other.
+type NUnit3Runtime =
+ /// Uses the runtime under which the assembly was built.
+ | Default
+ | V20
+ | V30
+ | V35
+ | V40
+ | V45
+ /// NUnit should use .NET framework but can select it's version
+ | Net
+ | Net20
+ | Net30
+ | Net35
+ | Net40
+ | Net45
+ /// NUnit should use Mono framework but can select it's version
+ | Mono
+ | Mono20
+ | Mono30
+ | Mono35
+ | Mono40
+ /// NUnit should use runtime specified by this value
+ | Other of string with
+ member x.ParamString =
+ match x with
+ | Default -> ""
+ | V20 -> "v2.0"
+ | V30 -> "v3.0"
+ | V35 -> "v3.5"
+ | V40 -> "v4.0"
+ | V45 -> "v4.5"
+ | Net -> "net"
+ | Net20 -> "net-2.0"
+ | Net30 -> "net-3.0"
+ | Net35 -> "net-3.5"
+ | Net40 -> "net-4.0"
+ | Net45 -> "net-4.5"
+ | Mono20 -> "mono-2.0"
+ | Mono30 -> "mono-3.0"
+ | Mono35 -> "mono-3.5"
+ | Mono40 -> "mono-4.0"
+ | Other(name) -> name
+
+/// Option which allows to specify if a NUnit error should break the build.
+type NUnit3ErrorLevel = TestRunnerErrorLevel
+
+/// The NUnit 3 Console Parameters type. FAKE will use [NUnit3Defaults](fake-testing-nunit3.html) for values not provided.
+///
+/// For reference, see: [NUnit3 command line options](https://github.com/nunit/nunit/wiki/Console-Command-Line)
+type NUnit3Params =
+ { /// The path to the NUnit3 console runner: `nunit3-console.exe`
+ ToolPath : string
+
+ /// The name (or path) of a file containing a list of tests to run or explore, one per line.
+ Testlist : string
+
+ /// An expression indicating which tests to run. It may specify test names, classes, methods,
+ /// catgories or properties comparing them to actual values with the operators ==, !=, =~ and !~.
+ /// See [NUnit documentation](https://github.com/nunit/nunit/wiki/Test-Selection-Language) for a full description of the syntax.
+ Where : string
+
+ /// Name of a project configuration to load (e.g.: Debug)
+ Config : string
+
+ /// Controls how NUnit loads tests in processes. See [NUnit3ProcessModel](fake-testing-nunit3-nunit3processmodel.html)
+ ProcessModel : NUnit3ProcessModel
+
+ /// Number of agents that may be allowed to run simultaneously assuming you are not running inprocess.
+ /// If not specified, all agent processes run tests at the same time, whatever the number of assemblies.
+ /// This setting is used to control running your assemblies in parallel.
+ Agents : int option
+
+ /// Controls how NUnit loads tests in processes. See: [NUnit3ProcessModel](fake-testing-nunit3-nunit3domainmodel.html).
+ Domain : NUnit3DomainModel
+
+ /// Allows you to specify the version of the runtime to be used in executing tests.
+ /// Default value is runtime under which the assembly was built. See: [NUnit3Runtime](fake-testing-nunit3-nunit3runtime.html).
+ Framework : NUnit3Runtime
+
+ /// Run tests in a 32-bit process on 64-bit systems.
+ Force32bit : bool
+
+ /// Dispose each test runner after it has finished running its tests
+ DisposeRunners : bool
+
+ /// The default timeout to be used for test cases. If any test exceeds the timeout value, it is cancelled and reported as an error.
+ TimeOut : TimeSpan
+
+ /// Set the random seed used to generate test cases
+ Seed : int
+
+ /// Specify the NUMBER of worker threads to be used in running tests.
+ /// This setting is used to control running your tests in parallel and is used in conjunction with the Parallelizable Attribute.
+ /// If not specified, workers defaults to the number of processors on the machine, or 2, whichever is greater.
+ Workers : int option
+
+ /// Causes execution of the test run to terminate immediately on the first test failure or error.
+ StopOnError : bool
+
+ /// Path of the directory to use for output files.
+ WorkingDir : string
+
+ /// File path to contain text output from the tests.
+ OutputDir : string
+
+ /// File path to contain error output from the tests.
+ ErrorDir : string
+
+ /// Output specs for saving the test results. Default value is TestResult.xml
+ /// Passing empty list does not save any result (--noresult option in nunit)
+ /// For more information, see: [NUnit3 command line options](https://github.com/nunit/nunit/wiki/Console-Command-Line)
+ ResultSpecs : string list
+
+ /// Tells .NET to copy loaded assemblies to the shadowcopy directory.
+ ShadowCopy : bool
+
+ /// Turns on use of TeamCity service messages.
+ TeamCity : bool
+
+ /// Default: [TestRunnerErrorLevel](fake-unittestcommon-testrunnererrorlevel.html).Error
+ ErrorLevel : NUnit3ErrorLevel
+ }
+
+/// The [NUnit3Params](fake-testing-nunit3-nunit3params.html) default parameters.
+///
+/// - `ToolPath` - The `nunit-console.exe` path if it exists in a subdirectory of the current directory.
+/// - `Testlist` - `""`
+/// - `Where` - `""`
+/// - `Config` - `""`
+/// - `ProcessModel` - `DefaultProcessModel`
+/// - `Agents` - `None`
+/// - `Domain` - `DefaultDomainModel`
+/// - `Framework` - `""`
+/// - `Force32bit` - `false`
+/// - `DisposeRunners` - `false`
+/// - `Timeout` - `2147483647 milliseconds`
+/// - `Seed` - `-1` (negative seed is ignored by NUnit and is not sent to it)
+/// - `Workers` - `None`
+/// - `StopOnError` - `false`
+/// - `WorkingDir` - `""`
+/// - `OutputDir` - `""`
+/// - `ErrorDir` - `""`
+/// - `ResultSpecs` - `"TestResult.xml"`
+/// - `ShadowCopy` - `false`
+/// - `TeamCity` - `false`
+/// - `ErrorLevel` - `Error`
+/// ## Defaults
+let NUnit3Defaults =
+ {
+ ToolPath = findToolInSubPath "nunit3-console.exe" (currentDirectory @@ "tools" @@ "Nunit")
+ Testlist = ""
+ Where = ""
+ Config = ""
+ ProcessModel = DefaultProcessModel
+ Agents = None
+ Domain = DefaultDomainModel
+ Framework = Default
+ Force32bit = false
+ DisposeRunners = false
+ TimeOut = TimeSpan.FromMilliseconds((float)Int32.MaxValue)
+ Seed = -1
+ Workers = None
+ StopOnError = false
+ WorkingDir = ""
+ OutputDir = ""
+ ErrorDir = ""
+ ResultSpecs = [currentDirectory @@ "TestResult.xml"]
+ ShadowCopy = false
+ TeamCity = false
+ ErrorLevel = Error
+ }
+
+/// Tries to detect the working directory as specified in the parameters or via TeamCity settings
+/// [omit]
+let getWorkingDir parameters =
+ Seq.find isNotNullOrEmpty [ parameters.WorkingDir
+ environVar ("teamcity.build.workingDir")
+ "." ]
+ |> Path.GetFullPath
+
+let buildNUnit3Args parameters assemblies =
+ let appendResultString results sb =
+ match results, sb with
+ | [], sb -> append "--noresult" sb
+ | x, sb when x = NUnit3Defaults.ResultSpecs -> sb
+ | results, sb -> (sb, results) ||> Seq.fold (fun builder str -> append (sprintf "--result=%s" str) builder)
+
+ new StringBuilder()
+ |> append "--noheader"
+ |> appendIfNotNullOrEmpty parameters.Testlist "--testlist="
+ |> appendIfNotNullOrEmpty parameters.Where "--where="
+ |> appendIfNotNullOrEmpty parameters.Config "--config="
+ |> appendIfNotNullOrEmpty parameters.ProcessModel.ParamString "--process="
+ |> appendIfSome parameters.Agents (sprintf "--agents=%i")
+ |> appendIfNotNullOrEmpty parameters.Domain.ParamString "--domain="
+ |> appendIfNotNullOrEmpty parameters.Framework.ParamString "--framework="
+ |> appendIfTrue parameters.Force32bit "--x86"
+ |> appendIfTrue parameters.DisposeRunners "--dispose-runners"
+ |> appendIfTrue (parameters.TimeOut <> NUnit3Defaults.TimeOut) (sprintf "--timeout=%i" (int parameters.TimeOut.TotalMilliseconds))
+ |> appendIfTrue (parameters.Seed >= 0) (sprintf "--seed=%i" parameters.Seed)
+ |> appendIfSome parameters.Workers (sprintf "--workers=%i")
+ |> appendIfTrue parameters.StopOnError "--stoponerror"
+ |> appendIfNotNullOrEmpty parameters.WorkingDir "--work="
+ |> appendIfNotNullOrEmpty parameters.OutputDir "--output="
+ |> appendIfNotNullOrEmpty parameters.ErrorDir "--err="
+ |> appendResultString parameters.ResultSpecs
+ |> appendIfTrue parameters.ShadowCopy "--shadowcopy"
+ |> appendIfTrue parameters.TeamCity "--teamcity"
+ |> appendFileNamesIfNotNull assemblies
+ |> toText
+
+let NUnit3 (setParams : NUnit3Params -> NUnit3Params) (assemblies : string seq) =
+ let details = assemblies |> separated ", "
+ traceStartTask "NUnit" details
+ let parameters = NUnit3Defaults |> setParams
+ let assemblies = assemblies |> Seq.toArray
+ if Array.isEmpty assemblies then failwith "NUnit: cannot run tests (the assembly list is empty)."
+ let tool = parameters.ToolPath
+ let args = buildNUnit3Args parameters assemblies
+ trace (tool + " " + args)
+ let result =
+ ExecProcess (fun info ->
+ info.FileName <- tool
+ info.WorkingDirectory <- getWorkingDir parameters
+ info.Arguments <- args) parameters.TimeOut
+ let errorDescription error =
+ match error with
+ | OK -> "OK"
+ | TestsFailed -> sprintf "NUnit test failed (%d)." error
+ | FatalError x -> sprintf "NUnit test failed. Process finished with exit code %s (%d)." x error
+
+ match parameters.ErrorLevel with
+ | DontFailBuild ->
+ match result with
+ | OK | TestsFailed -> traceEndTask "NUnit" details
+ | _ -> raise (FailedTestsException(errorDescription result))
+ | Error | FailOnFirstError ->
+ match result with
+ | OK -> traceEndTask "NUnit" details
+ | _ -> raise (FailedTestsException(errorDescription result))