Build a complete .NET Core solution on macOS using Visual Studio for Mac

Visual Studio for Mac provides a full-featured Integrated Development Environment (IDE) for developing .NET Core applications. This article walks you through building a .NET Core solution that includes a reusable library and unit testing.

This tutorial shows you how to create an application that accepts a search word and a string of text from the user, counts the number of times the search word appears in the string using a method in a class library, and returns the result to the user. The solution also includes unit testing for the class library as an introduction to unit testing concepts. If you prefer to proceed through the tutorial with a complete sample, download the sample solution. For download instructions, see Samples and Tutorials.

Note

Your feedback is highly valued. There are two ways you can provide feedback to the development team on Visual Studio for Mac:

  • In Visual Studio for Mac, select Help > Report a Problem from the menu or Report a Problem from the Welcome screen, which opens a window for filing a bug report. You can track your feedback in the Developer Community portal.
  • To make a suggestion, select Help > Provide a Suggestion from the menu or Provide a Suggestion from the Welcome screen, which takes you to the Visual Studio for Mac Developer Community webpage.

Prerequisites

For more information on prerequisites, see the .NET Core dependencies and requirements. For the full system requirements of Visual Studio 2019 for Mac, see Visual Studio 2019 for Mac Product Family System Requirements.

Building a library

  1. On the start window, select New Project. In the New Project dialog under the .NET Core node, select the .NET Standard Library template. This command creates a .NET Standard library that targets .NET Core as well as any other .NET implementation that supports version 2.1 of the .NET Standard. If you have multiple versions of the .NET Core SDK installed, you can choose different versions of .NET Standard for your library. Choose ".NET Standard 2.1" and select Next.

    Visual Studio for Mac New project dialog

  2. Name the project "TextUtils" (a short name for "Text Utilities") and the solution "WordCounter". Leave Create a project directory within the solution directory checked. Select Create.

    Visual Studio for Mac New project dialog options

  3. In the Solution pad, expand the TextUtils node to reveal the class file provided by the template, Class1.cs. Ctrl-click the file, select Rename from the context menu, and rename the file to WordCount.cs. Open the file and replace the contents with the following code:

    using System;
    using System.Linq;
    
    namespace TextUtils
    {
        public static class WordCount
        {
            public static int GetWordCount(string searchWord, string inputString)
            {
                // Null check these variables and determine if they have values.
                if (string.IsNullOrEmpty(searchWord) || string.IsNullOrEmpty(inputString))
                {
                    return 0;
                }
    
                // Convert the string into an array of words.
                var source = inputString.Split(new char[] { '.', '?', '!', ' ', ';', ':', ',' },
                                               StringSplitOptions.RemoveEmptyEntries);
    
                // Create the query. Use ToLowerInvariant to match uppercase/lowercase strings.   
                var matchQuery = from word in source
                                 where word.ToLowerInvariant() == searchWord.ToLowerInvariant()
                                 select word;
    
                // Count the matches, which executes the query. Return the result.
                return matchQuery.Count();
            }
        }
    }
    
  4. Save the file by using any of three different methods: use the keyboard shortcut +s, select File > Save from the menu, or ctrl-click on the file's tab and select Save from the contextual menu. The following image shows the IDE window:

    Visual Studio for Mac IDE window with class library file and method

  5. Select Errors in the margin at the bottom of the IDE window to open the Errors panel. Select the Build Output button.

    Bottom margin of the Visual Studio Mac IDE showing the Errors button

  6. Select Build > Build All from the menu.

    The solution builds. The build output panel shows that the build is successful.

    Visual Studio Mac Build output pane of the Errors panel with Build successful message

Creating a test project

Unit tests provide automated software testing during your development and publishing. The testing framework that you use in this tutorial is xUnit (version 2.4.0 or later), which is installed automatically when the xUnit test project is added to the solution in the following steps:

  1. In the Solution sidebar, ctrl-click the WordCounter solution and select Add > Add New Project.

  2. In the New Project dialog, select Tests from the .NET Core node. Select the xUnit Test Project followed by Next.

    Visual Studio Mac New Project dialog creating xUnit test project

  3. If you have multiple versions of the .NET Core SDK, you'll need to select the version for this project. Select .NET Core 3.1. Name the new project "TestLibrary" and select Create.

    Visual Studio Mac New Project dialog providing project name

  4. In order for the test library to work with the WordCount class, add a reference to the TextUtils project. In the Solution sidebar, ctrl-click Dependencies under TestLibrary. Select Edit References from the context menu.

  5. In the Edit References dialog, select the TextUtils project on the Projects tab. Select OK.

    Visual Studio Mac Edit References dialog

  6. In the TestLibrary project, rename the UnitTest1.cs file to TextUtilsTests.cs.

  7. Open the file and replace the code with the following code:

    using Xunit;
    using TextUtils;
    using System.Diagnostics;
    
    namespace TestLibrary
    {
        public class TextUtils_GetWordCountShould
        {
            [Fact]
            public void IgnoreCasing()
            {
                var wordCount = WordCount.GetWordCount("Jack", "Jack jack");
    
                Assert.NotEqual(2, wordCount);
            }
        }
    }
    

    The following image shows the IDE with the unit test code in place. Pay attention to the Assert.NotEqual statement.

    Visual Studio for Mac Initial unit test in the IDE main window

    It's important to make a new test fail once to confirm its testing logic is correct. The method passes in the name "Jack" (uppercase) and a string with "Jack" and "jack" (uppercase and lowercase). If the GetWordCount method is working properly, it returns a count of two instances of the search word. In order to fail this test on purpose, you first implement the test asserting that two instances of the search word "Jack" aren't returned by the GetWordCount method. Continue to the next step to fail the test on purpose.

  8. Open the Unit Tests panel on the right side of the screen. Select View > Tests from the menu.

    Visual Studio for Mac Unit Tests panel

  9. Click the Dock icon to keep the panel open. (Highlighted in the following image.)

    Visual Studio for Mac Unit Tests panel dock icon

  10. Click the Run All button.

    The test fails, which is the correct result. The test method asserts that two instances of the inputString, "Jack," aren't returned from the string "Jack jack" provided to the GetWordCount method. Since word casing was factored out in the GetWordCount method, two instances are returned. The assertion that 2 is not equal to 2 fails. This result is the correct outcome, and the logic of our test is good.

    Visual Studio for Mac test failure display

  11. Modify the IgnoreCasing test method by changing Assert.NotEqual to Assert.Equal. Save the file by using the keyboard shortcut Ctrl+s, File > Save from the menu, or ctrl-clicking on the file's tab and selecting Save from the context menu.

    You expect that the searchWord "Jack" returns two instances with inputString "Jack jack" passed into GetWordCount. Run the test again by clicking the Run Tests button in the Unit Tests panel or the Rerun Tests button in the Test Results panel at the bottom of the screen. The test passes. There are two instances of "Jack" in the string "Jack jack" (ignoring casing), and the test assertion is true.

    Visual Studio for Mac tests pass display

  12. Testing individual return values with a Fact is only the beginning of what you can do with unit testing. Another powerful technique allows you to test several values at once using a Theory. Add the following method to your TextUtils_GetWordCountShould class. You have two methods in the class after you add this method:

    [Theory]
    [InlineData(0, "Ting", "Does not appear in the string.")]
    [InlineData(1, "Ting", "Ting appears once.")]
    [InlineData(2, "Ting", "Ting appears twice with Ting.")]
    public void CountInstancesCorrectly(int count,
                                        string searchWord,
                                        string inputString)
    {
        Assert.NotEqual(count, WordCount.GetWordCount(searchWord,
                                                   inputString));
    }
    

    The CountInstancesCorrectly checks that the GetWordCount method counts correctly. The InlineData provides a count, a search word, and an input string to check. The test method runs once for each line of data. Note once again that you're asserting a failure first by using Assert.NotEqual, even when you know that the counts in the data are correct and that the values match the counts returned by the GetWordCount method. Performing the step of failing the test on purpose might seem like a waste of time at first, but checking the logic of the test by failing it first is an important check on the logic of your tests. When you come across a test method that passes when you expect it to fail, you've found a bug in the logic of the test. It's worth the effort to take this step every time you create a test method.

  13. Save the file and run the tests again. The casing test passes but the three count tests fail. This outcome is exactly what you expect to happen.

    Visual Studio for Mac expected test failure

  14. Modify the CountInstancesCorrectly test method by changing Assert.NotEqual to Assert.Equal. Save the file. Run the tests again. All tests pass.

    Visual Studio for Mac expected test passes

Adding a console app

  1. In the Solution sidebar, ctrl-click the WordCounter solution. Add a new Console Application project by selecting the template from the .NET Core > App templates. Select Next. Name the project WordCounterApp. Select Create to create the project in the solution.

  2. In the Solutions sidebar, ctrl-click the Dependencies node of the new WordCounterApp project. In the Edit References dialog, check TextUtils and select OK.

  3. Open the Program.cs file. Replace the code with the following code:

    using System;
    using TextUtils;
    
    namespace WordCounterApp
    {
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine("Enter a search word:");
                var searchWord = Console.ReadLine();
                Console.WriteLine("Provide a string to search:");
                var inputString = Console.ReadLine();
    
                var wordCount = WordCount.GetWordCount(searchWord, inputString);
    
                var pluralChar = "s";
                if (wordCount == 1)
                {
                    pluralChar = string.Empty;
                }
    
                Console.WriteLine($"The search word {searchWord} appears " +
                                  $"{wordCount} time{pluralChar}.");
            }
        }
    }
    
  4. Ctrl-click on the WordCounterApp project and select Run project from the context menu. When you run the app, provide values for the search word and input string at the prompts in the console window. The app indicates the number of times the search word appears in the string.

    Visual Studio for Mac console window showing your app running

  5. The last feature to explore is debugging with Visual Studio for Mac. Set a breakpoint on the Console.WriteLine statement: Select in the left margin of line 23, and you see a red circle appear next to the line of code. Alternatively, select anywhere on the line of code and select Run > Toggle Breakpoint from the menu.

    Visual Studio for Mac breakpoint set

  6. ctrl-click the WordCounterApp project. Select Start Debugging Project from the context menu. When the app runs, enter the search word "cat" and "The dog chased the cat, but the cat escaped." for the string to search. When the Console.WriteLine statement is reached, program execution halts before the statement is executed. In the Locals tab, you can see the searchWord, inputString, wordCount, and pluralChar values.

    Visual Studio for Mac debugger program execution stopped

  7. In the Immediate pane, type "wordCount = 999;" and press Enter. This command assigns a nonsense value of 999 to the wordCount variable showing that you can replace variable values while debugging.

    Visual Studio for Mac changing values in the immediate window

  8. In the toolbar, click the continue arrow. Look at the output in the console window. It reports the incorrect value of 999 that you set when you were debugging the app.

    Visual Studio for Mac continue button in the toolbar

    Visual Studio for Mac console window output

You can use the same process to debug code using your unit test project. Instead of starting the WordCount app project, ctrl-click the Test Library project, and select Start Debugging Project from the context menu. Visual Studio for Mac starts your test project with the debugger attached. Execution will stop at any breakpoint you've added to the test project, or the underlying library code.

See also