Debugging and Testing in C#

Debugging and testing are essential aspects of software development. They help ensure applications run smoothly and perform as expected.

By Tim TrottIntroduction to Programming with C# • October 22, 2008
1,302 words, estimated reading time 5 minutes.
Introduction to Programming with C#

This article is part of a series of articles. Please use the links below to navigate between the articles.

  1. Learn to Program in C# - Full Introduction to Programming Course
  2. Introdution to Programming - C# Programming Fundamentals
  3. Guide to C# Data Types, Variables and Object Casting
  4. C# Operators: Arithmetic, Comparison, Logical and more
  5. Application Flow Control and Control Structures in C#
  6. Introduction to Object Oriented Programming for Beginners
  7. Introduction to C# Object-Oriented Programming Part 2
  8. C# Collection Types (Array,List,Dictionary,HashTable and More)
  9. Error and Exception Handling in C#
  10. Events, Delegates and Extension Methods
  11. Complete Guide to File Handling in C# - Reading and Writing Files
  12. Introduction to XML and XmlDocument with C#
  13. What is LINQ? The .NET Language Integrated Query
  14. Introduction to Asynchronous Programming in C#
  15. Working with Databases using Entity Framework
  16. All About Reflection in C# To Read Metadata and Find Assemblies
  17. Debugging and Testing in C#
  18. Introduction to ASP.Net MVC Web Applications and C#
  19. Windows Application Development Using .Net and Windows Forms
  20. Assemblies and the Global Assembly Cache in C#
  21. Working with Resources Files, Culture & Regions in .Net
  22. The Ultimate Guide to Regular Expressions: Everything You Need to Know
Debugging and Testing in C#

In C# development, Visual Studio provides a powerful suite of debugging and testing tools that allow developers to identify issues, inspect the state of the application, and test different scenarios. We will explore the various debugging features available in Visual Studio, such as step into, step over, breakpoints, watches, locals, call stack, and more. We'll also discuss useful tools for testing and debugging in C# and provide code examples to illustrate how to use them effectively.

Introduction to Debugging in C#

Debugging is the process of identifying, analyzing, and removing errors (bugs) in a software application. Visual Studio provides a rich set of tools for debugging C# applications, making it easier to understand what's happening during execution, diagnose issues, and fix them. When an error occurs, or the application doesn't behave as expected, debugging can help trace the problem's source by allowing developers to step through the code and observe how the application state changes.

Using the Visual Studio Debugger

Visual Studio and the Express editions have a very powerful debugging tool that features a step-through debugger, a watch list, and a call stack, among other features. These are all essential tools for debugging and testing applications.

The most basic feature of the debug environment is breakpoints. When the program is run in debug mode, the application will pause and return to the editor when it hits a breakpoint. This will allow you to see what the program is doing at that execution stage. You can step through the code to see where the flow of execution moves; you can inspect variables, view the call stack and evaluate expressions.

Breakpoints

Breakpoints can be set in several ways; the easiest is to click on the gutter (the grey area to the left of the code editor). This will highlight the line red with a circle in the gutter. You can only set a breakpoint on a valid line of executable code. You can also right-click on the code line and set a breakpoint or use the Debug menu to add breakpoints. They can be removed in the same way as you added them.

Breakpoints in action
Breakpoints in action

When you run the program in debug mode (F5), the program will pause just before the breakpoint and return to the code editor window. From here, you can investigate the cause of the problem by interrogating the variables or stepping through the code. You can see the contents of a variable, object or class by hovering over the variable with the mouse. The editor will pop up a window containing detailed information about the object. We will cover interrogating variables more in the "Watches" section below.

Breakpoint variable investigation
Breakpoint variable investigation

You can step through the code in several different methods; you can step through line by line using F11, step over using F10 or step out using Shift + F11.

Step Through: Every line of code executed will be debugged. When a method call is invoked, the flow enters the method and returns to the calling line after completion.

Step Over: As above, however, you will not debug internal method calls. This is a better debug tool if you know a method is working and want to call it without debugging.

Step Out: If you entered a method using Step Through, Step Out will return you to the point that the method was called.

Have a play with the debugger using this code:

C#
using System;

class Program
{
  static void Main()
  {
    int a = testMethod1();
    int b = testMethod2();
    Console.WriteLine(a + b);    
  }

  public int testMethod1()
  {
    int x = 1;
    int y = 2;
    return (x * y);
  }

  public int testMethod2()
  {
    int x = 5;
    int y = 10;
    return (x + y);
  }
}

Set a breakpoint on the call to testMethod1 in Main. Start debugging using F5 and step through the program. Have a go at investigating variables with the mouse. Step through the code and see how the flow moves into testMethod1, back to main and then into testMethod2.

Close the program and start debugging again; use the step-over and notice the difference. The debugger only stops on each line of Main but steps over the code inside the methods.

Start debugging again (last time) and step in again. When you get inside testMethod1, use step out to return to the main method.

Conditional Breakpoints

By default, a breakpoint will stop program execution every time they are hit. You can, however, set a breakpoint only to stop when a certain condition is met. By right-clicking on the breakpoint, you can add conditions so that the debugger will not stop at the breakpoint if the condition is not met. You can also tell the debugger to perform additional actions when triggering a breakpoint, like displaying a message or running a macro. You can also view the "hit count", useful when debugging an iteration or recursive method.

Watches, Auto's and Locals

Watches, Auto and Locals are windows that are available during debugging. You can add a "watch", which will display the value of the selected variable in the watch's window, and you can see how the value changes as you step through the code. This is particularly useful for debugging a recursive or iterative method as you can see how all the values interact or look at the loop counter.

Auto's and locals have automatically generated watch lists that contain the variables available in the current context.

One of the more useful features of autos and watches is that you can modify the contents of a variable during debugging. When inspecting a variable, you can right-click on it and select "Edit Value". Hitting enter will update the value and allow you to continue testing with a different value.

Call Stack

The Call Stack window displays the list of methods that have been called to reach the current point in execution. It allows you to trace back the chain of method calls that led to the current line. To access the call stack, go to the Debug menu, then Windows, then click Call Stack to open the Call Stack Window.

Using the following code, set a breakpoint in the Add method. When you hit the breakpoint in debug mode, the Call Stack will show how Add was called from the Main method.

C#
public static void Main()
{
    int result = Add(5, 10);
    Console.WriteLine(result);
}

public static int Add(int a, int b)
{
    return a + b;
}
Example of how the call stack shows the calling function
Example of how the call stack shows the calling function

Debug Class

Microsoft .Net contains a class called Debug, which you can use to log debug messages in the output window. These debug messages will only be available while the program runs under debug mode in the IDE. If it is compiled under release, these debug messages will be removed.

To use the Debug class, add "using System.Diagnostics;" to your uses section and call Debug.Write:

C#
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Diagnostics;

namespace ConsoleApplication3
{
    class Program
    {
        static void Main(string[] args)
        {
            Image image = Bitmap.FromFile("c:breakpoints.png");

            if (image.Height > 100)
            {
                Debug.Write("Image height is too large");
            }
            else
            {

            }
        }
    }
}

In addition to Write, you can call WriteIf, WriteLine, and WriteLineIf. WriteIf and WriteLineIf will only write the debug message if the condition is met. WriteLine appends a line terminator to the message.

C#
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Diagnostics;

namespace ConsoleApplication3
{
    class Program
    {
        static void Main(string[] args)
        {
            Image image = Bitmap.FromFile("c:breakpoints.png");

            Debug.WriteIf(image.Height > 100, "Image height is too large");
        }
    }
}
Debug.Write example
Debug.Write example

Immediate Window

The immediate window is a powerful tool for debugging as it allows you to inspect variables, method return values, and construct and execute statements. Virtually any single-line statement can be run, inspected and modified.

Visual Studio Immediate Window
Visual Studio Immediate Window

This can be particularly useful when formatting a string or counting indexes for a substring, as shown above. You can make quick changes easily to get the desired result without recompiling.

Conclusion

Debugging is an integral part of the software development process, and Visual Studio provides an extensive set of tools that make this task easier and more efficient. From basic breakpoints to advanced tools like Call Stack, Watch Window, and Attaching to Processes, Visual Studio offers all the capabilities needed to debug C# applications effectively.

About the Author

Tim Trott is a senior software engineer with over 20 years of experience in designing, building, and maintaining software systems across a range of industries. Passionate about clean code, scalable architecture, and continuous learning, he specialises in creating robust solutions that solve real-world problems. He is currently based in Edinburgh, where he develops innovative software and collaborates with teams around the globe.

Related ArticlesThese articles may also be of interest to you

CommentsShare your thoughts in the comments below

My website and its content are free to use without the clutter of adverts, popups, marketing messages or anything else like that. If you enjoyed reading this article, or it helped you in some way, all I ask in return is you leave a comment below or share this page with your friends. Thank you.

There are no comments yet. Why not get the discussion started?

New comments for this post are currently closed.