VB.NET Console SQL Server Unit Tests

Neil HaddleyMay 27, 2023

Test driven development

Unit tests are automated tests written and run by software developers to ensure that a section of an application (known as the "unit") meets its design and behaves as intended.

I wanted to add unit tests to a Visual Basic Console App that fetched data from a database using SqlConnection, SqlCommand, SqlDataAdapter and DataTable classes.

In any case it is best practice is for Unit Tests to run independent of a running database server.

I created a new project (in a new solution)

I created a new project (in a new solution)

I selected the .NET Framework (old school)

I selected the .NET Framework (old school)

I selected .NET Framework 4.7.2

I selected .NET Framework 4.7.2

Main

Main

I added Hello World code

I added Hello World code

I updated the Console Application to connect to a database and display the contents of a Tasks table

I updated the Console Application to connect to a database and display the contents of a Tasks table

I added a second project to the solution. I selected the Unit Test Project (.NET Framework) template

I added a second project to the solution. I selected the Unit Test Project (.NET Framework) template

I accepted the defaults

I accepted the defaults

I added a reference to the Test project

I added a reference to the Test project

ChatGPT

I asked ChatGPT to generate unit tests for the code.

Write Unit Tests for this code

Imports System.Data.SqlClient

Module Module1

...

To write unit tests for the given code, you can use a testing framework such as NUnit or xUnit. However, since the code primarily consists of database interactions and console output, it may not be feasible to write traditional unit tests for it.

Instead, you can consider refactoring the code to improve testability by separating concerns. One approach could be to move the database-related code into a separate class or method that returns the result DataTable. Then, you can write unit tests specifically for that database-related code.

Here's an example of how you can refactor the code and write unit tests for the database interaction part using NUnit:

...

Database Tests

Database Tests

Refactoring the code with testing in mind

As ChatGPT explained it is hard to create Unit Tests for the database and Console code contained in the Module1.vb.

It does not help that the database access and Console.WriteLine code is mixed together in a single block.

I extracted the WriteDataTable code to create a function that was easier to create Unit Tests for.

I wrote the UnitTestProject2 tests with the help of ChatGPT.

The WriteDataTable function expects to be passed a DataTable.

The WriteDataTable function expects to be passed a DataTable.

Running the Unit Tests

Running the Unit Tests

UnitTest1.vb

TEXT
1Imports System.Data.SqlClient
2Imports System.Text
3Imports Microsoft.VisualStudio.TestTools.UnitTesting
4
5
6<TestClass()> Public Class DatabaseTests
7    Private ReadOnly connectionString As String = "Data Source=(local);Initial Catalog=ToDo;user id=sa;password=Passw0rd123"
8
9    <TestMethod()>
10    Public Sub GetData_ShouldReturnValidDataTable()
11        ' Arrange
12        Dim database As New DatabaseInteraction(connectionString)
13
14        ' Act
15        Dim result As DataTable = database.GetData()
16
17        ' Assert
18        Assert.IsNotNull(result)
19        Assert.IsTrue(result.Rows.Count > 0)
20    End Sub
21End Class
22
23Public Class DatabaseInteraction
24    Private ReadOnly connectionString As String
25
26    Public Sub New(connectionString As String)
27        Me.connectionString = connectionString
28    End Sub
29
30    Public Function GetData() As DataTable
31        Dim query As String = "SELECT * FROM Items"
32        Dim result As DataTable = Nothing
33
34        Using connection As New SqlConnection(connectionString)
35            connection.Open()
36            Using command As New SqlCommand(query, connection)
37                Using adapter As New SqlDataAdapter(command)
38                    result = New DataTable()
39                    adapter.Fill(result)
40                End Using
41            End Using
42        End Using
43
44        Return result
45    End Function
46End Class

Module1.vb

TEXT
1Imports System.Data.SqlClient
2
3Public Module Module1
4
5    Private ReadOnly connectionString As String = "Data Source=(local);Initial Catalog=ToDo;user id=sa;password=Passw0rd123"
6
7    Sub Main()
8        Console.WriteLine("Tasks")
9
10        Dim query As String = "SELECT * FROM Items"
11        Dim result As DataTable = Nothing
12        Using connection As New SqlConnection(connectionString)
13            connection.Open()
14            Using command As New SqlCommand(query, connection)
15                Using adapter As New SqlDataAdapter(command)
16                    result = New DataTable()
17                    adapter.Fill(result)
18
19                    WriteDataTable(result)
20
21                End Using
22            End Using
23        End Using
24
25        Console.ReadLine()
26    End Sub
27
28    Public Function WriteDataTable(result As DataTable)
29        Console.WriteLine("{0,10}{1,20}{2,7}", "ID", "Description", "Done")
30
31        Dim count As Integer = 0
32        For Each row As DataRow In result.Rows
33            Console.WriteLine("{0,10}{1,20}{2,7}", row("ID"), row("Description"), row("Done"))
34            count = count + 1
35        Next
36
37        Return count
38    End Function
39End Module

UnitTestProject1 - UnitTest1.vb

TEXT
1mports System.Data.SqlClient
2Imports System.Text
3Imports Microsoft.VisualStudio.TestTools.UnitTesting
4
5<TestClass()> Public Class UnitTest1
6
7    Private ReadOnly connectionString As String = "Data Source=(local);Initial Catalog=ToDo;user id=sa;password=Passw0rd123"
8
9    Private _dataTable As New DataTable()
10
11    <TestInitialize>
12    Public Sub intialize()
13
14        'reset the test data before each test method is executed
15        _dataTable = New DataTable()
16
17        ' Create the example result DataTable
18        _dataTable.Columns.Add("ID", GetType(Integer))
19        _dataTable.Columns.Add("Description", GetType(String))
20        _dataTable.Columns.Add("Done", GetType(Boolean))
21
22        _dataTable.Rows.Add(1000, "Feed Dog", False)
23
24    End Sub
25
26    <TestMethod>
27    Public Sub TestDataFromDataTable()
28
29        ' Act
30        Dim count = ConsoleApp1.WriteDataTable(_dataTable)
31
32        ' Assert
33        Assert.IsNotNull(_dataTable)
34        Assert.IsTrue(_dataTable.Rows.Count = 1)
35        Assert.IsTrue(count = _dataTable.Rows.Count)
36    End Sub
37
38    <TestMethod>
39    Public Sub TestDataFromDataTableAddItem()
40
41        'Arrange 
42        _dataTable.Rows.Add(1001, "Feed Cat", False)
43
44        ' Act
45        Dim count = ConsoleApp1.WriteDataTable(_dataTable)
46
47        ' Assert
48        Assert.IsNotNull(_dataTable)
49        Assert.IsTrue(_dataTable.Rows.Count = 2)
50        Assert.IsTrue(count = _dataTable.Rows.Count)
51    End Sub
52
53
54    <TestMethod>
55    Public Sub TestDataFromDataTableAddTwoItems()
56
57        'Arrange 
58        _dataTable.Rows.Add(1001, "Feed Snake", False)
59        _dataTable.Rows.Add(1001, "Feed Mouse", False)
60
61        ' Act
62        Dim count = ConsoleApp1.WriteDataTable(_dataTable)
63
64        ' Assert
65        Assert.IsNotNull(_dataTable)
66        Assert.IsTrue(_dataTable.Rows.Count = 3)
67        Assert.IsTrue(count = _dataTable.Rows.Count)
68    End Sub
69
70
71    <TestMethod>
72    Public Sub SelectDataFromDatabaseTest()
73        ' Arrange
74        Dim query As String = "SELECT * FROM Items"
75        Dim result As DataTable = Nothing
76        Dim count As Integer = 0
77
78        ' Act
79        Using connection As New SqlConnection(connectionString)
80            connection.Open()
81            Using command As New SqlCommand(query, connection)
82                Using adapter As New SqlDataAdapter(command)
83                    result = New DataTable()
84                    adapter.Fill(result)
85
86                    count = ConsoleApp1.WriteDataTable(result)
87
88                End Using
89            End Using
90        End Using
91
92        ' Assert
93        Assert.IsNotNull(result)
94        Assert.IsTrue(result.Rows.Count > 0)
95        Assert.IsTrue(count = result.Rows.Count)
96    End Sub
97
98
99End Class

UnitTestProject2 - UnitTest1.vb

TEXT
1Imports System.Data.SqlClient
2Imports System.IO
3Imports ConsoleApp1
4
5<TestClass()> Public Class DatabaseTests
6    Private ReadOnly connectionString As String = "Data Source=(local);Initial Catalog=ToDo;user id=sa;password=Passw0rd123"
7
8    <TestMethod()>
9    Public Sub GetData_ShouldReturnValidDataTable()
10        ' Arrange
11        Dim database As New DatabaseInteraction(connectionString)
12
13        ' Act
14        Dim result As DataTable = database.GetData()
15
16        ' Assert
17        Assert.IsNotNull(result)
18        Assert.IsTrue(result.Rows.Count > 0)
19    End Sub
20End Class
21
22Public Class DatabaseInteraction
23    Private ReadOnly connectionString As String
24
25    Public Sub New(connectionString As String)
26        Me.connectionString = connectionString
27    End Sub
28
29    Public Function GetData() As DataTable
30        Dim query As String = "SELECT * FROM Items"
31        Dim result As DataTable = Nothing
32
33        Using connection As New SqlConnection(connectionString)
34            connection.Open()
35            Using command As New SqlCommand(query, connection)
36                Using adapter As New SqlDataAdapter(command)
37                    result = New DataTable()
38                    adapter.Fill(result)
39                End Using
40            End Using
41        End Using
42
43        Return result
44    End Function
45End Class
46
47
48
49
50<TestClass()> Public Class Module1Tests
51    Private ReadOnly connectionString As String = "Data Source=(local);Initial Catalog=ToDo;user id=sa;password=Passw0rd123"
52
53    <TestMethod>
54    Public Sub WriteDataTable_ShouldWriteCorrectData()
55        ' Arrange
56        Dim result As New DataTable()
57        result.Columns.Add("ID", GetType(Integer))
58        result.Columns.Add("Description", GetType(String))
59        result.Columns.Add("Done", GetType(Boolean))
60        result.Rows.Add(1, "Task 1", True)
61        result.Rows.Add(2, "Task 2", False)
62
63        Dim expectedOutput As String =
64            "        ID         Description   Done" + vbCrLf +
65            "         1              Task 1   True" + vbCrLf +
66            "         2              Task 2  False" + vbCrLf
67
68        ' Act
69        Dim consoleOutput As String = CaptureConsoleOutput(Sub() Module1.WriteDataTable(result))
70
71        Console.WriteLine("Expected")
72        Console.WriteLine("<" + expectedOutput + ">")
73        Console.WriteLine("Test Result")
74        Console.WriteLine("<" + consoleOutput + ">")
75
76        ' Assert
77        Assert.AreEqual(expectedOutput, consoleOutput)
78    End Sub
79
80    ' Helper method to capture console output during testing
81    Private Function CaptureConsoleOutput(action As Action) As String
82        Dim originalOut As TextWriter = Console.Out
83        Dim writer As New StringWriter()
84        Console.SetOut(writer)
85        action.Invoke()
86        Console.SetOut(originalOut)
87        Return writer.ToString()
88    End Function
89End Class