NUnit test how to prepare/modify function - vb.net

Probably simple question but i am beginner in this area. Have NUnit project and would like to test similar functions like this example below. As you see i pass some id to it and then looping through, when data found i delete records.
Now with regards to test i would like instead of delete i would like to collect all of those id's (those which are going to be deleted) and compare them in my test project.
What i got on mind is to extend my function with additional variables to store id's then add additional paraemeter to function like isTest as Boolean and in places where are deletes put statment like: if Test then not delete but add ids to variables, but i think its very bad idea.
How this should be accomplished right way?
Public Sub DelEverythingAssociatedWithSection(secId As Integer)
Using con As New SqlConnection(strcon)
con.Open()
Using transaction = con.BeginTransaction
Try
Dim dtHtmlSection_KatSubkatDAL As New DataTable
dtHtmlSection_KatSubkatDAL = CType(New HtmlSection_KatSubkatDAL().GetAllBySecId(secId), DataTable)
If dtHtmlSection_KatSubkatDAL IsNot Nothing Then
For Each sec_katsubkat As DataRow In dtHtmlSection_KatSubkatDAL.Rows
Dim dtSubSec_SecKatSubKat_SubSubKat As New DataTable
dtSubSec_SecKatSubKat_SubSubKat = CType(New DALSubSec_SecKatSubKat_SubSubKat().GetAllBySec_KatSub(CInt(sec_katsubkat(0))), DataTable)
If dtSubSec_SecKatSubKat_SubSubKat IsNot Nothing Then
For Each subsec As DataRow In dtSubSec_SecKatSubKat_SubSubKat.Rows
Dim dtHtmlSentence_SubSec_SecKatSubKat_SubSubKat As New DataTable
dtHtmlSentence_SubSec_SecKatSubKat_SubSubKat = CType(New HtmlSentence_SubSec_SecKatSubKat_SubSubKatDAL().GetAllBySubSec_SecKatSubKat_SubSubKat(CInt(subsec(0))), DataTable)
If dtHtmlSentence_SubSec_SecKatSubKat_SubSubKat IsNot Nothing Then
For Each sent As DataRow In dtHtmlSentence_SubSec_SecKatSubKat_SubSubKat.Rows
Dim dtArtikel_Beschreibung As New DataTable
dtArtikel_Beschreibung = CType(New Artikel_BeschreibungDAL().GetAllBySentence(CInt(sent(0))), DataTable)
If dtArtikel_Beschreibung IsNot Nothing Then
For Each artBesch As DataRow In dtArtikel_Beschreibung.Rows
Call New Artikel_BeschreibungDAL().Delete(CInt(artBesch(0)), transaction)
Next
End If
Call New HtmlSentence_SubSec_SecKatSubKat_SubSubKatDAL().Delete(CInt(sent(0)), transaction)
Next
End If
Call New DALSubSec_SecKatSubKat_SubSubKat().Delete(CInt(subsec(0)), transaction)
Next
End If
Call New HtmlSection_KatSubkatDAL().Delete(CInt(sec_katsubkat(0)), transaction)
Next
End If
Call New DALSection().Delete(secId, transaction)
'If we made it this far without an exception, then commit.
transaction.Commit()
Catch ex As Exception
transaction.Rollback()
Throw 'Rethrow exception.
End Try
End Using
End Using
End Sub

I think you are making it too complicated. Just write a test like this... (pseudo-code)
// Arrange
Set up a section with some data.
Assume.That(section exists with data) // optional, in case setup fails
// Act
Delete the section
// Assert
Try to get the data in the section
Assert.That(no data is found)

Related

calling a VB.net Function for SQL recordset

Beginner here
I have the following code which I would like to call using a button called findCustomerBTN
Public Function Execute(ByVal sqlQuery As String) As ADODB.Recordset
If SecuritySSPIchkbx.Checked Then
chk = "TRUE"
Else chk = "FALSE"
End If
builder.DataSource = ServerBox.Text
builder.InitialCatalog = DatabaseBox.Text
builder.UserID = Username.Text
builder.Password = Password.Text
builder.IntegratedSecurity = chk
MessageBox.Show(builder.ConnectionString)
Using sqlConnection1 As New SqlConnection(builder.ConnectionString)
sqlConnection1.Open()
Try
command = New SqlCommand(sqlQuery, sqlConnection1)
adapter.SelectCommand = command
adapter.Fill(ds, "Create DataView")
adapter.Dispose()
command.Dispose()
sqlConnection1.Close()
dv = ds.Tables(0).DefaultView
DataGridView1.DataSource = dv
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Using
End Function
How do I call this function:
Private Sub findCustomerBTN_Click(sender As Object, e As EventArgs) Handles
sqlquery = "Select * from customers where name = 'Smith'"
call function?
End Sub
I have googled but I can't wrap my head around how a function works any pointers to help me understand would be great thanks
In VB, a function is a block of code that returns a value. Your code does not return a value, and the type of query execution you're carrying out will never return an ADODB.RecordSet - that's an ancient technology remeniscent of the VB6 era, and you're using a much more modern data access strategy (ADO.NET, DataTables and DataAdapter) though it's not the latest and greatest.
To offer a run through of your code, and the other issues it has:
Execute is a pretty bland name - go for something more specific, just incase you run the wrong execute and some poor prisoner ends up in front of the firing squad
Your function takes an sql string as a parameter to run, but then overwrites it with a fixed string, so there isn't much point offering it as a parameter in the first place. I could call Execute("SELECT * FROM kittens") expecting to get some cute data back, and all I get is the same old customer
Avoid calling MessageBox.Show in any code that shoudl reasonably be expected to run quietly and repeatedly, otherwise the user is going to get hella annoyed. If youre putting this here for debugging purposes, learn how the visual studio debugger works instead
Your code runs an sql query and assigns the resulting data table data to the datasource of a grid, so that the grid will show the data. There's absolutely no need for this code to be a function (and in c# it wouldnt even compile because it doesn't return a value
What are functions? What do they do? They take some inuts and return some outputs:
Public Function AddTheseTwo(a as Integer, b as Integer) As Integer
Return a + b
End Function
They are called like this:
Dim sum = AddTheseTwo(2, 3)
I.e. you give the name of the function and the input values, which can be variables, and store the result (usually, because you want to use it). Here's a code of yours that is a sub - a block of code that doesn't return a value
Private Sub findCustomerBTN_Click(sender As Object, e As EventArgs) Handles
Execute("Select * from customers where name = 'Smith'")
End Sub
It's not linked to your button click, because there's nothing afte rthe Handles keyword. It should be something like Handles findCustomerBTN.Click. You can mash that button all day and nothing will happen
It called Execute but didn't store the return value (because it didn't need to, because Execute doesn't return anything, so Execute should have been declared as a sub, not a function)
Edit:
You mentioned you want the function to return a datatable:
Public Function GetDataTable(ByVal sqlQuery As String) As DataTable 'need to Imports System.Data if you haven't already
If SecuritySSPIchkbx.Checked Then
chk = "TRUE"
Else chk = "FALSE"
End If
'better to declare builder in this function, not elsewhere
builder.DataSource = ServerBox.Text
builder.InitialCatalog = DatabaseBox.Text
builder.UserID = Username.Text
builder.Password = Password.Text
builder.IntegratedSecurity = chk
MessageBox.Show(builder.ConnectionString)
Using sqlConnection1 As New SqlConnection(builder.ConnectionString)
sqlConnection1.Open()
Try
'note: better to declare adapter and command in this function too
command = New SqlCommand(sqlQuery, sqlConnection1)
adapter.SelectCommand = command
Dim dt as New DataTable
adapter.Fill(dt)
adapter.Dispose()
command.Dispose()
sqlConnection1.Close()
Return dt
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Using
Return Nothing 'a function has to return something from all possible code paths, even if it's Nothing :)
End Function
And then you call it like this, perhaps:
Private Sub findCustomerBTN_Click(sender As Object, e As EventArgs) Handles whateverbutton.Click
'you can set a datatable as a datasource, doesn't have to be the datatable.defaultview
myDataGRidView.DataSource = GetDataTable("Select * from customers where name = 'Smith'")
End Sub
I recommend you turn on the options for Strict/Explicit etc, to encourage better coding practices. By default VB is quite loose, letting you use variables that havent been declared (autodeclaring variable names that are a typo of another variable name etc), automatically returning Nothing for you from functions etc - it's these little auto's that will later lead to bugs and frsutrations. Computer programming si a precise art; turn on all options to force yourself to be as precise as possible
You can call Execute function using this code:
Private Sub findCustomerBTN_Click(sender As Object, e As EventArgs) Handles
Execute("Select * from customers where name = 'Smith'")
End Sub
You also have to remove this line from Execute function:
sqlQuery = ("select * from ac_billbook where ref = '900123'")
Please follow Steve suggestion to read a good book about programming in VB.NET.

Adding Data to DataTable

This code is recording the inserted IDs in arraylist and when press on button it should create loop for this array and get the data of each ID from the array and put it in Datatable and add the new data while looping to the datatable and finaly show it in datagridview .
The problem in the result when I insert one record it works fine but when I insert more than one the datagridview shows just the last one , what the mistake that I Done ?!!
In Mainform
Public Inserted_record_hold_dt As New DataTable
Public Inserted_record_dt As New DataTable
Public Sub Addcolumnstodatagrid()
Inserted_record_dt.Columns.Add("ID")
Inserted_record_dt.Columns(0).AutoIncrement = True
Inserted_record_dt.Columns.Add("drawingname")
Inserted_record_dt.Columns.Add("serial")
End Sub
and call this in main_Load
Addcolumnstodatagrid()
And this in the show button when click to loop on the array list that already have the latest ID's that has been added
Private Sub show_btn_Click(sender As System.Object, e As System.EventArgs) Handles show_btn.Click
Dim InsertedID As Integer
Inserted_record_dt.Clear()
Dim R As DataRow = Inserted_record_dt.NewRow
'Loop For each ID in the array "Inserted_List_Array"
For Each InsertedID In mainadd.Inserted_List_Array
'MsgBox(InsertedID.ToString)
Dim cmd As New SqlCommand("select drawingname , serial from main where drawingid = '" & InsertedID & "'", DBConnection)
DBConnection.Open()
Inserted_record_hold_dt.Load(cmd.ExecuteReader)
Try
R("drawingname") = Inserted_record_hold_dt.Rows(0).Item(0)
R("serial") = Inserted_record_hold_dt.Rows(0).Item(1)
Inserted_record_dt.Rows.Add(R)
Catch
End Try
'MsgBox("added")
DBConnection.Close()
cmd = Nothing
Inserted_record_hold_dt.Clear()
Next
sendmail.Show()
sendmail.Mail_DGView.DataSource = Inserted_record_dt
End Sub
Please tell me what is the problem in my code .
Your mistake is in declaring the R variable just one time outside the loop. In this way you continuously replace the values on the same instance of a DataRow and insert always the same instance.
Just move the declaration inside the loop
For Each InsertedID In mainadd.Inserted_List_Array
......
Try
Dim R As DataRow = Inserted_record_dt.NewRow
R("drawingname") = Inserted_record_hold_dt.Rows(0).Item(0)
R("serial") = Inserted_record_hold_dt.Rows(0).Item(1)
Inserted_record_dt.Rows.Add(R)
Catch
....
Next
Another important thing to do is to remove the empty Try/Catch because you are just killing the exception (no message, no log) and thus you will never know if there are errors in this import. At the end you will ship a product that could give incorrect results to your end user.

SyncLock not working in unit tests

I've got a Module that I'm wanting to use to cache some stuff. It's pretty simple. I wanted to shy away from the ConcurrentDictionary because it needs to be a guaranteed operation.
Public Module SchemaTableCache
Private lockObject As New Object
Private columnCache As New Dictionary(Of String, SortedSet(Of String))
<Extension>
Public Sub CacheSchemaTable(dataReader As IDataReader, name As String)
SyncLock lockObject
Dim rows As New List(Of DataRow)
If columnCache.ContainsKey(name) Then
Return
End If
rows = dataReader.GetSchemaTable().Rows.OfType(Of DataRow)().ToList()
columnCache.Add(name, New SortedSet(Of String)(rows.Select(Function(r) r.Field(Of String)("ColumnName"))))
End SyncLock
End Sub
<Extension>
Public Function HasColumn(name As String, column As String) As Boolean
SyncLock lockObject
Dim cols As New SortedSet(Of String)
If Not columnCache.TryGetValue(name, cols) Then
Return False
End If
Return cols.Contains(column)
End SyncLock
End Function
End Module
Here's the thing. I have some unit tests that test the code that leverages the HasColumn function. I set these tests up like this:
dataReader.Setup(Function(x) x(field)).Returns(val)
' setup the schema table
Dim table As New DataTable()
table.Columns.Add("ColumnName", GetType(String))
If setupTable Then
table.Rows.Add(field)
End If
dataReader.Setup(Function(x) x.GetSchemaTable()) _
.Returns(table)
dataReader.Object.CacheSchemaTable("table")
Then they test this function:
Dim typeName = GetType(T).Name
Debug.WriteLine($"IDataReader_Value({schemaTableName}.{column})")
If Not schemaTableName.HasColumn(column) Then
Debug.WriteLine($"Could not find column {column}; returning default value.")
Return typeName.DefaultValue()
End If
Dim input = dr(column)
Debug.WriteLine($"Found column {column}; returning value {input}.")
Return Value(Of T)(input)
You can see here where I hit the HasColumn method. Here's the thing. If I execute these tests individually they succeed; however, they fail if I execute the entire set of tests.
Clearly there is a thread-safety issue here, but I can't for the life of me figure out what I did wrong. Can somebody help me see where I went wrong?
The output of a test when it's failing is:
Test Name: IDataReader_ValueBoolean
Test Outcome: Failed
Result Message: Assert.AreEqual failed. Expected:<True>. Actual:<False>.
Result StandardOutput:
Debug Trace:
IDataReader_Value(table.field)
Could not find column field; returning default value.
The output of a test when it succeeds is:
Test Name: IDataReader_ValueBoolean
Test Outcome: Passed
Result StandardOutput:
Debug Trace:
IDataReader_Value(table.field)
Found column field; returning value True.
I figured it out. The issue wasn't with SyncLock, it was just with my logic. Each test is hitting a different problem. Some are testing the missing column, while some are expecting it to exist. Because of this I needed to be able to update the cache.
Here is the new logic:
SyncLock lockObject
Debug.WriteLine($"Caching schema table {name}.")
Dim rows As New List(Of DataRow)
If Not columnCache.ContainsKey(name) Then
Debug.WriteLine($"Adding cache key for {name}.")
columnCache.Add(name, New SortedSet(Of String)())
End If
rows = dataReader.GetSchemaTable().Rows.OfType(Of DataRow)().ToList()
Debug.WriteLine($"Schema table rows count: {rows.Count}")
columnCache(name) = New SortedSet(Of String)(rows.Select(Function(r) r.Field(Of String)("ColumnName")))
Debug.WriteLine($"Successfully cached {name}.")
End SyncLock

Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: chunkLength

I am trying to use Parallel.ForEachLoop for a iterations of almost million records in My windows application. I am facing an error while fails on a convert to string on a string builder due to some threading problem although i use a object for lock.
I Tried looking at shared resource for Parallel.ForEach could not find a proper answer.
dtProd has 900 000 records
Dim sbFile As New StringBuilder
messagesLock As Object = New Object()
Dim sbRecord As New StringBuilder
Dim dtDet As New Data.DataTable
Dim dtProd As New Data.DataTable
Public Sub CreateFeedFile()
Try
GetData()
Dim temporaryEnumerable As IEnumerable(Of DataRow) = dtProd.Rows.Cast(Of DataRow)()
sbRecord.AppendLine(dtFeed(0).Item("HeaderText"))
Parallel.ForEach(temporaryEnumerable, Sub(dtDet)
RunLoop(DetCount)
End Sub)
sbRecord.AppendLine(dtFeed(0).Item("FooterText"))
Catch ex As Exception
Dim a = ex.Message.ToString()
End Try
End Sub
Private Sub RunLoop(ByRef DetCount As Integer)
For Each drDet As DataRowView In dvDet 'loop detail records of field values
.. .. .. Append info to sbRecord
Next
Try
SyncLock Me.messagesLock
sbFile.AppendLine(sbRecord.ToString())
sbRecord.Clear()
End SyncLock
Catch ex As Exception
Dim a = ex.Message.ToString() --Fails Here on the statement sbRecord.ToString()
End Try
The problem is that you have one StringBuilder (sbRecord) that is shared by the parallel threads. You need to move sbRecord to be a local variable inside RunLoop.
I think you also want the .AppendLine(...) calls in CreateFeedFile to be on sbFile rather than sbRecord.

"System.NullReferenceException" when trying to fill array

This is my code:
Public UserBookings() As String
If dr.Read() Then
Dim intCounter As Integer
intCounter = 0
While dr.Read()
UserBookings(intCounter) = dr(0)
intCounter = intCounter + 1
End While
Return UserBookings
Else
Return False
End If
But I get this error at runtime:
A first chance exception of type 'System.NullReferenceException' occurred in Project.exe
And the array doesn't even seem to fill up.
Anything I can do to fix this? Bear in mind it is a dynamic array (I'm not sure how many entries it will have) so I don't see how I can loop through every entry and give it an initial value.
Thanks.
Among the problems listed as possible answers here, I also see that you are returning False at one point when return type of the method is string. That is not allowed. You can return Nothing or String.Empty, but not False.
It seems as though you dr(0) is an empty object, but try adding .ToString() to the end of it. But of course it depends on what datatype UserBookings() is.
Upon speaking with my colleague
you should probably use dr.HasRows instead of "if dr.read()" because you are losing an entire row.
The easiest option here is to realize that you can use an alternative construct such as an ArrayList (or better, a List(of String))
This way, the object will dynamically grow until you've loaded all the items, and you can return the result as an array
Two possible options below (the generic List(Of x) is preferred.
Function ReadArray(ByVal dr As IDataReader) As String()
Dim tempstorage As New List(Of String)
While dr.Read()
tempstorage.Add(dr(0))
End While
Return tempstorage.ToArray()
End Function
--
Function ReadArray2(ByVal dr As IDataReader) As String()
Dim tempstorage As New ArrayList()
While dr.Read()
tempstorage.Add(dr(0))
End While
Return tempstorage.ToArray(GetType(String))
End Function
--
Note: I've added some additional code (assuming that dr is a DataReader, and that you're calling this as a function