matteskolin


Visual Studio Tips

C# Interactive in VS Code Part 1

Posted by matteskolin on

Microsoft introduced the Interactive Window for C# into Visual Studio in Visual Studio 2015. This feature provides a REPL shell that allows you to run c# in a scripting mode.

REPL stands for Read-Evaluate-Print-Loop.

This is useful for things like trying out unfamiliar langugage features, classes, or small snippets of code in a clean environment.

To access the interactive windows in Visual Studio Proper, navigate to

View -> Other Windodws -> C# Interactive


This same functionality can be accessed in VS Code via the dotnet-script command. Note currently this is not included when installing the .NET CLI. It needs to be added as a global tool with the following command.

dotnet tool install --global dotnet-script 

Link to github here

https://github.com/filipw/dotnet-script


To Start a REPL session similar to the C# interactive window in VS Code, run the scripting tool from the VS Code powershell terminal.

dotnet-script

Here we use the REPL to declare and initialize a list of integers and display the sum. C# statements end with a semicolon as normal, but you can also evaluate expressions and the reuslt will be printed as we see in the total below.

PS C:\src\SandBox\CSharpScriptHelpers> dotnet-script
> var sales = new List<int>(){234,235,765,453,234,234,234,222};
> var total = sales.Sum();
> total
2611
>   


The above example was rather, simple. What do we do when we want to script using classes in other assemblies or with more complex initialization?

Using .CSX Scripting Files in Interactive

.csx files are C# scripting files that allow you to run C# code more like a scripting langugage. Code does not have to be in classes or namespaces, although you can use classes as part of the script.

Instead of typing each line individually into the REPL, you can execute an entire block of code at once by loading a .csx file. Unlike normal .cs code files, these files do not require that code be contained in namespaces. Statements can be written outside of functions,

Let’s define our example above in a file called SalesTotal.csx below.

While using a csx file, it is easier to define functions and load other scripts or assemblies. When using just the REPL, if you make a mistake you often have to reset everything and start over. Using a csx file allows you to get your scripting just right before executing it in the C# interpretor.

//SalesTotal.csx

    //define function for calcuating standard deviation
    public static double StdDev(IEnumerable<int> list)
    {
        // ref: https://stackoverflow.com/questions/2253874/linq-equivalent-for-standard-deviation
        // ref: http://warrenseen.com/blog/2006/03/13/how-to-calculate-standard-deviation/ 
        var mean = 0.0;
        var sum = 0.0;
        var stdDev = 0.0;
        var n = 0;
        foreach (var value in list)
        {
            n++;
            var delta = value - mean;
            mean += delta / n;
            sum += delta * (value - mean);
        }
        if (1 < n)
            stdDev = Math.Sqrt(sum / (n - 1));

        return stdDev; 

    }



 var sales = new List<int>(){234,235,1765,453,234,234,234,222};


 var total = sales.Sum();
 var average = sales.Average();
var stdDev = StdDev(sales);

 
Console.WriteLine($"Total Sales: {total}");
Console.WriteLine($"Sales StdDev: {stdDev}");

Visual Studio Tips

Use Tasks for Easy Test Filtering in VS Code

Posted by matteskolin on

Automated tests should be discoverable and easy to run. In this post, I will show a way to make it easier to run tests in VSCode using Tasks. I will use the XUnit testing framework to run test against a .NET Core console application.

The full source code for this project can be found Here.

The Project that will be tested

I created a class with three methods. The AddInts and the SubtractInts methods are simple methods that contain logic that is unit testable.

The third method UpdateDatabaes() is a method that, because it updates a databaes and is therefore dependent on an system external to to the method, is a good candidate for an integration tests. Integration tests are tests that test the interaction among discrete components of a system, whereas unit tests are best used to test the behavior of a single method.

public class Foo
{
    public static int AddInts(int a, int b)
    {
        return a + b;
    }

    public static int SubtractInts(int a, int b)
    {
        return a - b;
    }

    public static bool UpdateDatabase()
    {
        //Example of an integration test that interacts with a database

        //Add Code here to update a database 

        return true;

    }
}

The Tests

I have created one class for integration tests and one class for unit tests. The Xunit TraitAttribute can be used on classes to assign a property to the test. This test metadata is used when we run the test to filter the tests by the Trait value, in this case either the Integration or Unit categories.

This way, we can seperate the execution of our unit and integration tests into seperate commands. This is important because we want to be able to run our unit tests frequently and quickly in order to detect new bugs early, and to not be spending too much time waiting around for the tests to run.

Integration tests, on the other hand, tend to be slower, especially since they may require the entire app to be loaded into memory, databases to be started and populated, networks to be setup, etc. Because of this, having the option to run integration tests seperately from the unit tests is helpful.

We will accomplish this by using the .NET CLI and the –filter flag. We will then make things even easier for us by using the VSCode tasks system, and optionally the Task Runner extension, to make running the filtered tests into a single click.

    [Trait("Category","Integration")]
    public class IntegrationTests 
    {
        [Fact]
        public void UpdateDatabaseTest()
        {
            //Arrange //Act
            var result = Foo.UpdateDatabase();

            Assert.True(result);
        }

    }

    [Trait("Category","Unit")]
    public class UnitTests 
    {
        [Fact]
        public void AddIntsTest()
        {
            var result = Foo.AddInts(3,1);
            Assert.Equal(4,result);
        }

        [Fact]
        public void SubtractIntsTest()
        {
            var result = Foo.SubtractInts(1,-3);
            Assert.Equal(4,result);
        }

        [Fact]
        public void SubtractInts2Test()
        {
            var result = Foo.SubtractInts(1,0);
            Assert.Equal(1,result);
        }
    }


Create the tasks.json

I have added two tasks labeled “Run Unit Tests”, and “Run Integration Tests” to my /.vscode/tasks.json file. For more information about VSCode tasks, checkout the microsoft docs here.

These tasks call the .NET CLI dotnet test command with the filter argument which itself takes a filter expression in which we can use the traits we defined earlier in our test code.

The –no-build flag is optional as you may want to build your project before executing the tests, but I like to only build the project when truly necessary, and don’t mind running a seperate build command before running my tests on updated code.

{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "Run Unit Tests",
            "command": "dotnet",
            "type": "process",
            "args": [
                "test",
                "--filter",
                "Category=Unit",
                "--no-build"
            ],
            "problemMatcher": "$msCompile"
        },
        {
            "label": "Run Integration Tests",
            "command": "dotnet",
            "type": "process",
            "args": [
                "test",
                "--filter",
                "Category=Integration",
                "--no-build"
            ],
            "problemMatcher": "$msCompile"
        }
    ]
}

Running The Tests

There a couple options for how to run the tasks..

1) Use the comand palette

Press CONTROL + SHIFT + P to open the command palette. Select “Tasks: Run Task”. All of the tasks you have defined in tasks.json above should appear in this list. Select the desired tasks and press enter.







2) Use the Task Runner extension.

I reall like this task runner extension which displays a list of available tasks in the explorer window. Mine looks like this.. When I want to run my Unit Tests, I just click the button. I don’t need to type anything in the command line or fumble through the command palette