Have you ever written code to generate code? Probably.
Have you ever written proper integration tests for that? Probably not.
There is now a follow up article that uses the latest .Net project format in Visual Studio 2017. This post is still displaying the now deprecated project.json configuration
Everyone's done it - write code that writes code. Be it for converting from an esoteric Domain Specific Language, generating from a set of known data or for any other reason. It happens a lot, it's useful a lot, but it's not tested a lot. Testing such code has always been inconvenient, did involve a lot of disk IO and quite often relied on scripts and manual work. Luckily, with dotnets Roslyn compiler, there are NuGet packages for code analysis and compilation available that make all of this so easy!
The complete sample repository is available at GitHub. Here are the interesting parts:
It starts with the CodeGenerator which does what it's name implies - it creates code. In this case, it's a simple class that has one method: AddIntegers().
This project.json is a regular, xUnit enabled, configuration file. There is a reference to the Microsoft.CodeAnalysis.CSharp package. That one will bring the Roslyn API to your project.
The actual integration test class is quite big, so let's break it down into it's functional units:
- GenerateCode() does just that - it gets the string representation of the sourcecode
- CreateCompilation() takes the sourcecode, adds assembly references and turns it into a CSharpCompilation object
- CompileAndLoadAssembly() now invokes the Roslyn API to compile onto a MemoryStream, then loads it and makes the content available
- Finally, CallCalculatorMethod() uses reflection on the newly generated assembly and invokes the AddIntegers() method
This is a really interesting approach on tackling code generation. While I got the initial bits to set this up from Tugberk Ugurlus blog, I've had a project of my own where I did some heavy code generation (for correcting Xml documents) where I needed that. There's also code that works both in .Net Core and the full .Net framework.