I have a new Linux laptop and I wanted to run C# code. I had no idea where to start. I last wrote C# for money in 2004. It took me over an hour of hunting to figure out how to run a single test, so I decided to write a tutorial that could help someone else go from zero to NUnit with Visual Studio Code.

I’m especially interested in knowing how to package these setup instructions to make them repeatable, since I don’t know enough yet about how to do infrastructure as code.

Assumptions

For this tutorial, I assume that you know nothing about the modern .NET environment. You don’t know how to install anything, where to find it, nor how to use it. You might know the following:

I think I need to use Visual Studio or something like it. I’ve heard of VS Code.

I know I want to write tests with NUnit.

I know that I need to add assembly references to my C# project in order to use NUnit.

You’ve never installed any of this stuff before—or, at least, you’ve never knowingly nor intentionally installed it.

Environment

I’m running Pop!_OS on a System 76 Galago Pro laptop. You can safely think of this as Ubuntu 18.04. To my knowledge, I have nothing about .NET installed at all.

I use etckeeper in order to be able to roll back the contents of /etc on my file system. I use stow in order to be able to roll back the contents of my home directory’s dotfiles.

The Goal

I have one immediate goal: I have some C# code with NUnit tests and I want to be able to run those tests. The C# code consists of two classes and has no entry point (“main”). I judge success by being able to run these tests as see the test results.

Instructions

I wanted to install VS Code, create a project, paste in some C# code, and then run the tests. I tried to do exactly that, it didn’t work, and I gathered enough information to figure out how to make it work. Now I have reordered those instructions to make them appear more orderly and intentional.

Next, I launch the newly-installed application named “Code”. It launches, then “helpfully” opens a web page telling me how to get started, forcing me to put focus back on the actual application that I launched.

Opt Out of Telemetry Reporting

So before I have the faintest idea how to use VS Code to do anything, a helpful window pops up telling me that I’m going to send “telemetry information” to Microsoft as I use VS Code. No, thanks.

Now I have to learn about putting VS Code settings under version control, because I know that configuration settings in tools that I don’t know well can easily spiral out of control in minutes, and then I waste hours trying to get back on track.

Thank you, Microsoft.

Fortunately, this doesn’t hurt too badly. First, I go to $HOME/.config to find a new directory Code. This seems to contain some combination of configuration data and temporary storage for VS Code. I want to put some of this stuff in version control, but not all, because programmers don’t care enough about separating configuration settings (I want in version control) from temporary storage (I definitely don’t want in version control).

Since I use stow for my dotfiles, and I had previously stowed my .config directory, $HOME/.config really points to $DOTFILES/common/config/.config, where DOTFILES is the root of a git repository and my “all my dotfiles” project. That git repository ignores common/config/.config in general, because of so many high-churn files, so I have to choose carefully which files inside .config I want to track in that git repository.

I dig inside $DOTFILES/common/config/.config/Code and find user settings file called User/settings.json. I want to keep versions of this.

I learned later that User/settings.json doesn’t exist until you add a user-level preference setting to override the system-level preferences. Great!

In VS Code, I choose File > Preferences > Settings. This opens an editor window on an empty JSON document describing your user-level preferences. I save this file before changing it. This last action creates the file User/settings.json, which I commit to my dotfiles repository before I start changing things and fall into a ditch somewhere.

# Inside VS Code
Press Ctrl+P to launch VS Code Quick Open
Paste "ext install ms-vscode.csharp" into the little dialog.
[A window pops up to show evidence that there is now a C# extension installed.]

Create The VS Code Project

Since tools like VS Code always have magical wizards to create new projects and since I don’t know much about the contents of a C# project, I ask VS Code to create a new project, so that I can delete most of it and replace it with the code I want to run.

And… of course, VS Code doesn’t have a menu item for “new project”, so I hunt down how to do that. Happily, the .NET SDK makes that relatively painless and I like doing this from the transparency of the command line.

$ cd $WORKSPACES_ROOT # The path where I like to put all my projects.
$ dotnet new console -o $PROJECT_NAME # PROJECT_NAME is a directory name
[Evidence of creating a project.]
# Verify
$ find $PROJECT_NAME
[Program.cs and some object files and a project file of type .csproj.]

Next, I put these assets into version control, but again, I don’t know what I’m doing, so I rely on gitignore.io to generate .gitignore for me.

I open this project in VS Code by opening Explorer (View > Explorer), pressing Open Folder, then choosing $PROJECT_NAME, meaning the root of the project that I created with dotnet new.

Right away, VS Code tries to build the project, and it pops up a window telling me that the project needs some more assets to build the project, so I agree to add those assets by pressing Yes. Since that adds things that .gitignore appears not to ignore, I look at them to decide for myself whether I want to track them in version control.

The file .vscode/launch.json appears to have launch commands and that seems handy to track. The file .vscode/tasks.json appears to have a build command and that seems handy to track. I decide to track them both.

Build And Run The Project With VS Code

I go back inside VS Code and choose Tasks > Run Build Task, then enter build for the name of the task. VS Code shows evidence of building the project. This task seems to correspond with the entry in .vscode/tasks.json, so I learn something there.

Next, I choose Debug > Start Without Debugging.

The UX specialists at Microsoft decided that Run would confuse programmers, so instead they chose to name the menu item Start Without Debugging and put it under the menu item Debug.

This tells me a lot about what the average .NET programmer expects. Not debugging becomes the exception, rather than the rule.

This also reminds me of sudo add-apt-repository –remove {repository_name}. Srsly.

I see Hello World! in the Debug Console, so now I can run this project from both the command line and with a single keystroke (Ctrl+F5).

Run Tests With NUnit

This step requires some extra work and I had to hunt down the details. I naively installed something and didn’t know that it depended on something else and so on. I hope that this saves you some headache.

By installing all these packages into the project, I could run tests, at least from the command line. Almost.

$ dotnet test
Program.cs(7,21): error CS0017: Program has more than one entry point defined. Compile with /main to specify the type that contains the entry point. [$PROJECT_ROOT/$PROJECT_NAME.csproj]

I believe that I only have one entry point, so I tried for a few minutes to figure out this message, but I gave up due to not finding anything simple and straightforward to read. Instead, I now simply remove Program.cs and replace it with the code that I want to run, which contains tests.

I can even run the tests in VS Code. I open the file containing the tests, wait a moment, and see Run All Tests as an option beneath the [TestFixture] annotation where I declare the test class. When I choose this option, VS Code runs the tests and reports the same results as dotnet test.

With this, I feel satisfied. I can run tests.

Removing It All

In order to remove all this stuff, this seems to suffice. First, I close VS Code. After that I issue these commands.

Future Work

I’m not planning to work in C# on VS Code in a professional setting, so this sufficed for me. Even so, I can imagine wanting to add at least the following.

Run this entire setup in a container of some kind to isolate it from the rest of the system. Docker?

Somebody must have built some kind of decent GUI test runner that runs inside VS Code to run these tests. What plays the role of Test-Driven .NET for VS Code?

How do I organize the files in a C#/NUnit project? I presume that I want separate assemblies for production code and test code, so that I can ship the first without the second.

If you can write or point to a tutorial that helps answer these questions, then please do. I would happily expand this tutorial to include information like that.

By The Way…

I learned something about dpkg the hard way while preparing this tutorial: I don’t trust the word remove in dpkg –remove to mean “remove”, because it doesn’t necessarily remove things, at least according to my understanding of “remove”.

Strange. I expect –remove to remove things. I guess not. After about 15 minutes of searching the web without even knowing which specific keywords would help me—I love that—I stumble upon a clue examining the switches on dpkg. I find a switch called –purge.

Wait a moment! If –remove removes things, then why do you need –purge? This must mean that –remove only sometimes removes things or it removes things for some weaker meaning of “remove”. I figured that it couldn’t hurt to try.

If you remove something with dpkg, but it doesn’t seem to completely disappear, then try purging it. If you understand this better, then please explain it in the comments so that I can improve this document.