]> Posts for July 2020 🌐:aligrant.com

Capturing console outputs with Microsoft Test Framework

Alastair Grant | Monday 20 July 2020

Recently I've been really appreciating the MSTest Framework included with Visual Studio, and have been using it more and more over other unit test frameworks; it works out of the box, simple to use and very easy to write effective tests.  Visual Studio will also stub out your unit tests for you for a given class.  And to top if off, you can associate tests with TFS/Azure DevOps test cases.

I am currently working on a console application which takes a number of arguments, this is something that has always been frustrating to test as traditionally one would either need to change the debug arguments for each run, or run scripts against a built exe, which meant no debugging.  I had the bright idea of calling the Console Program.Main() function directly from a unit test, and thus allow me to script all variations on arguments needed, but there were a couple of considerations that needed to be made.

The first thing to do is to make the Program class and the Main method public, which is not required for a Console App to run, but required for another assembly to gain visibility (there are other ways around this if it's a real issue, but setting the visibility to public is the quickest).  And that's about it, you can now run unit tests against your console application entry point.

Capturing Console Output

You might though, want to test the outcome of your inputs.  A console application is fairly enclosed and you're likely to be spitting data out to Standard Output, and not returning data from methods.  Which brings up the next question, how do we see what is being output by the console application?

.NET makes it easy and we can redirect the Standard Output to other locations through a TextWriter.  But for those experienced with MSTest will have realised that the output is already redirected to the test logs.  And in fact, if we try to override this in your ClassInitialize method not a lot happens as MSTest will hijack the console feed again.  This can be worked-around by moving the setup closer into your TestInitialise method, I've used a StringBuilder:

[TestClass]
public class MyTests
{
    private StringBuilder ConsoleOutput { get; set; }
    
    [TestInitialize]
    public void Init()
    {
        Console.SetOut(new StringWriter(this.ConsoleOutput));    // Associate StringBuilder with StdOut
        this.ConsoleOutput.Clear();    // Clear text from any previous text runs
    }
}

Each time a  test is executed, the TestInitialize method is executed first, reassigning our TextWriter and clearing out the previous value (we could also put the clear out in a TestCleanup decorated method).

Now whenever Console.Write is called (or anything else that writes to the standard output stream), the text will be added into the StringBuilder.  This is a fairly blunt approach and essentially dumps the screen output into a string, which you can then search for the data you're interested in, e.g.

Assert.IsTrue(this.ConsoleOutput.ToString().Contains("Expected output"));

A more surgical approach would be to implement your own TextWriter that you can analyse line-by-line as-it-happens.

Recording test outputs

As noted above, by default the test framework normally captures the Standard Output, which is presented in the results of the test.  Fortunately for us the Test Framework also captures the default Tracer, so you can get output by calling Debug.WriteLine().

If you really need to capture the Console Standard Output as a test report, then you will need to resort to proxying two TextWriter instances, capturing Console.Out in setup and adding it with your own TextWriter.  I don't think there is a class like this already in the framework, but it would be trivial to put together if needed.

Breaking from the voyeuristic norms of the Internet, any comments can be made in private by contacting me.

Using Powershell to get the current user's group membership

Alastair Grant | Wednesday 1 July 2020

Every now and then I want to know what groups a user is a member of in a domain attached computer in PowerShell. The traditional answer to this approach is to use the Get-ADPrincipalGroupMembership cmdlet, which does precisely this.  The catch with using this approach is that it requires the Active Directory module for Windows PowerShell to be installed; this is a feature that can be added via Roles and Features but is not installed by default.

Fortunately, when it comes to the current user, we don't need to go to that module, we can instead turn to .NET and run a very simple function call:

[System.Security.Principal.WindowsIdentity]::GetCurrent().Groups

This will give the claims of the current user, including domain groups and nested groups.  The catch is you only get SIDs returned for this, which is less helpful for us humans to read.

To resolve the SIDs you can pipe the output into a loop to convert it to a more readable format:

[System.Security.Principal.WindowsIdentity]::GetCurrent().Groups | ForEach-Object -Process { Write-Host $_.Translate([System.Security.Principal.NTAccount]) }

The above will simply print out the object name for each group, put whatever logic you want in the -Process argument.

Breaking from the voyeuristic norms of the Internet, any comments can be made in private by contacting me.

Entries for: July 2020

Previous