4

I have read the answers to Do you put unit tests in same project or another project? and the consensus back then (almost 10 years ago) is to put unit tests in a separate project from the code they are testing. The main reason given is to avoid non-production code from being deployed, which is fairly sound.

Things have moved on a lot since then, and at least for JavaScript based projects, having our tests next to the code is the norm, and indeed most consider it highly desirable. But I won't go into the advantages here as it's not what this question is about.

Assuming we want the same thing for C#, but still avoid deploying test code to prod, is there some post-compilation magic we can weave at the IL level to strip out the tests from the binary as part of the build process? For example could we search and destroy all classes from the binary with the [TestFixture] attribute?

Could we harness some feature of Roslyn to achieve the same?

Alternatively, is there some other approach we can take?

NightOwl888
  • 55,572
  • 24
  • 139
  • 212
Mike Chamberlain
  • 39,692
  • 27
  • 110
  • 158
  • 1
    You could simple add a preprocessor directive, e.g. `#if INCLUDETESTS` (why specifically *"post-compilation"*?) – Manfred Radlwimmer Mar 07 '18 at 11:33
  • 3
    So, you want the code you deploy to be different to the code that was tested? Why would that be desirable? – Damien_The_Unbeliever Mar 07 '18 at 11:34
  • 1
    I would advise against trying to do this because it breaks one of the main reasons of running the tests in the first place: **You know your code works**. If you then remove the tests post build, you're effectively deploying a different, untested version of your code. – Zhaph - Ben Duguid Mar 07 '18 at 11:34
  • 1
    @ManfredRadlwimmer indeed, or just `#if DEBUG` ;) Another option would be to decorate the methods with `[Conditional("INCLUDETESTS")]` and the compiler will remove them from the build. – Zhaph - Ben Duguid Mar 07 '18 at 11:38
  • I've got to comment to the anonymous downvoter, I think this is a perfectly good question, especially with which the way this question was purposefully phrased. – Mike Chamberlain Mar 11 '18 at 16:40

1 Answers1

6

One option is to use MSBuild conditions to exclude the test files from the final build.

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFrameworks>netstandard2.0</TargetFrameworks>
  </PropertyGroup>

  <ItemGroup>
    <Compile Condition=" '$(Configuration)' == 'Release' " Remove="Test\**\*.cs" />
  </ItemGroup>

</Project>

This could all be done with a single statement using globs, which is simpler than having to put an #if DEBUG statement in every single test file.

Of course, that means you need to build again after you test in order to apply the conditional compilation and it effectively means the changes applied in Release mode are untested. Since floating point rounding errors and other compiler optimizations aren't tested, you may miss something important in your tests.

NOTE: Unlike what the case may be in JavaScript, it is highly desirable in .NET to separate the test code from the production code so you don't have to re-compile before deployment, effectively negating any testing you may have done.

NightOwl888
  • 55,572
  • 24
  • 139
  • 212