Debugging and Testing in C#Debugging and testing are essential aspects of software development. They help ensure applications run smoothly and perform as expected.
This article is part of a series of articles. Please use the links below to navigate between the articles.
- Learn to Program in C# - Full Introduction to Programming Course
- Introdution to Programming - C# Programming Fundamentals
- Guide to C# Data Types, Variables and Object Casting
- C# Operators: Arithmetic, Comparison, Logical and more
- Application Flow Control and Control Structures in C#
- Introduction to Object Oriented Programming for Beginners
- Introduction to C# Object-Oriented Programming Part 2
- C# Collection Types (Array,List,Dictionary,HashTable and More)
- Error and Exception Handling in C#
- Events, Delegates and Extension Methods
- Complete Guide to File Handling in C# - Reading and Writing Files
- Introduction to XML and XmlDocument with C#
- What is LINQ? The .NET Language Integrated Query
- Introduction to Asynchronous Programming in C#
- Working with Databases using Entity Framework
- All About Reflection in C# To Read Metadata and Find Assemblies
- Debugging and Testing in C#
- Introduction to ASP.Net MVC Web Applications and C#
- Windows Application Development Using .Net and Windows Forms
- Assemblies and the Global Assembly Cache in C#
- Working with Resources Files, Culture & Regions in .Net
- The Ultimate Guide to Regular Expressions: Everything You Need to Know

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.

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.

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:
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.
public static void Main()
{
int result = Add(5, 10);
Console.WriteLine(result);
}
public static int Add(int a, int b)
{
return a + b;
}

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:
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.
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");
}
}
}

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.

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.