How to Assert a list in unit test? - vb.net

I have this test method where I am testing a list. It is working fine but I want to simplify the Assert.
<TestMethod()> Public Sub Method_Scenario_ReturnsList()
'Arrange
Dim sut As New ClassName()
'Act
Dim result = sut.ListMethod(parameter)
'Assert
Assert.AreEqual("1", result(0).ID)
Assert.AreEqual("One", result(0).Name)
Assert.AreEqual("2", result(1).ID)
Assert.AreEqual("Two", result(1).Name)
End Sub
Is there a way to simplify this in just two lines of Assert where I check all the IDs and all the Names? Something like
CollectionAssert.AreEqual({"1","2"}, result.Select(Of ))
Is there a syntax to do that? I am noob in VB.net

You can use
CollectionAssert.AreEqual({"1","2"}, result.Select(Function(item) item.Id).ToArray())
CollectionAssert.AreEqual({"One","Two"}, result.Select(Function(item) item.Name).ToArray())

Related

Unit Test failing when comparing two objects in VS 2012

I have the following unit test:
Public Sub pickWeaponTest_Rock()
Dim lastMove As String = "Rock"
Dim actualSelectedMove As Weapon = New Paper()
Dim result As Weapon
Dim target As Object = New PlayerComputerTactical() ' TODO: Initialize to an appropriate value
result = target.pickWeapon()
Assert.AreEqual(result, actualSelectedMove)
Assert.Inconclusive("A method that does not return a value cannot be verified.")
End Sub
I get the following error, even though both are Paper objects:
Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException was unhandled by user code
Message=Assert.AreEqual failed. Expected:. Actual:.
Could someone please shed some light as to how to get this working.
You're comparing two different Paper objects.
You need to override Equals() and GetHashCode() to consider them equal.
However, if you want all instances of the class to be equal, you should use an enum instead.

VB.NET equivalent of a nameless variable in C#?

In C#, you can do this:
new MyClass().MyMethod();
The method is executed and the reference is (typically) discarded since no reference to the object is kept.
My question is: Is this possible with VB.NET (.NET v4)?
Edit: I suppose this is a better example:
new Thread((x) => doSomething()).Start();
Is this even possible in VB.NET?
VB.NET has stricter rules about the syntax of a statement. The curly-brace languages allow any expression to also be a statement, simply by terminating it with a semi-colon. That's not the case for VB.NET. You can only use this syntax if the method you call is a Function. Which allows you to use the assignment statement:
Dim result = New Test().Func()
If it is a Sub then you'll have to use the assignment statement to store the object reference. This otherwise has no runtime effect, the reference is optimized away.
In addition to Hans' answer, you could use a With statement:
Sub Main
With New Person("Ahmad")
.PrintName()
.Name = "Mageed"
.PrintName()
End With
End Sub
Public Class Person
Public Property Name As String
Public Sub New(ByVal name As String)
Me.Name = name
End Sub
Public Sub PrintName()
Console.WriteLine("Name: {0} - Len: {1}", Name, Name.Length)
End Sub
End Class
It's not as succinct as C#, but the reference to the object is discarded after End With.
If you're explicitly wanting to call a sub and not a function, you can:
Call (New obj).Func()
Which will anonymously create a new obj, and call its Func() method
You can do lambda functions in VB.NET like this:
Dim test = Function (x)
x.doStuff()
End Function
Which would be semantically equivilent to:
var test = (x) => x.doStuff();
I think the one constraint though is that it must return a result under VB.NET.

How to write a simple Expression-like class in .NET 2.0?

I'm currently working in .NET 2.0 Visual Basic. The current project is an Active Directory Wrapper class library within which I have a Searcher(Of T) generic class that I wish to use to search the underlying directory for objects.
In this Searcher(Of T) class I have the following methods:
Private Function GetResults() As CustomSet(Of T)
Public Function ToList() As CustomSet(Of T)
Public Function Find(ByVal ParamArray filter() As Object) As CustomSet(Of T)
// And some other functions here...
The one that interests me the most is the Find() method to which I can pass property and values and would like to parse my LDAP query from this filter() ParamArray parameter. Actually, all I can figure out is this:
Public Sub SomeSub()
Dim groupSearcher As Searcher(Of Group) = New Searcher(Of Group)()
Dim groupsSet as CustomSet(Of Group) = groupSearcher.Find("Name=someName", "Description=someDescription")
// Working with the result here...
End Sub
But what I want to be able to offer to my users is this:
Public Sub SomeSub()
Dim groupSearcher As Searcher(Of Group) = New Searcher(Of Group)()
Dim groupsSet As CustomSet(Of Groupe) = groupSearcher.Find(Name = "someName", Guid = someGuid, Description = "someDescription")
// And work with the result here...
End Sub
In short, I want to offer some kind of Expression feature to my users, unless it is too much work, as this project is not the most important one and I don't have like 2 years to develop it. I think that the better thing I should do is to write something like CustomExpression that could be passed in parameters to some functions or subs.
Thanks for any suggestions that might bring me to my goal!
Interesting question. This is a language dependent feature, so I don't see this happening without some clever trickery of the IDE/compiler.
You could however have optional overloads on your Find method (vb.net is good for this), then make the search string manually to obtain the result.
Finally you could make use of lambda functions, but only in .net 3.5 and above. Even still, it would require your searcher to expose a preliminary set of data so you can recover the expression tree and build up the find string.
UPDATE
I've just been playing around with Reflection to see if I can retrieve the parameters passed, and build up a string dynamically depending on if they exist. This doesn't appear to be possible, due to the fact that compiled code doesn't reference the names.
This code just used was:
'-- Get all the "parameters"
Dim m As MethodInfo = GetType(Finder).GetMethod("Find")
Dim params() As ParameterInfo = m.GetParameters()
'-- We now have a reference to the parameter names, like Name and Description
Hmm. http://channel9.msdn.com/forums/TechOff/259443-Using-SystemReflection-to-obtain-parameter-values-dynamically/
Annoyingly it's not (easily) possible to recover the values sent, so we'll have to stick with building up the string in a non-dynamic fashion.
A simple optional method would look like:
Public Sub Find( _
Optional ByVal Name As String = "", _
Optional ByVal Description As String = "")
Dim query As String = String.Empty
If Not String.IsNullOrEmpty(Name) Then
query &= "Name=" & Name
'-- ..... more go here with your string seperater.
End If
End Sub

VB.NET - Adding more than 1 string to .contains

I have an HTMLElementCollection that I'm going through using a For Each Loop to see if the InnerHTML contains certain words. If they do contain any of those keywords it gets saved into a file.
Everything works fine but I was wondering if there is a way to simplify. Here's a sample
For Each Helement As HtmlElement In elements
If Helement.InnerHtml.Contains("keyword1") Or Helement.InnerHtml.Contains("keyword2") Or Helement.InnerHtml.Contains("keyword3") Or Helement.InnerHtml.Contains("keyword4") Or Helement.InnerHtml.Contains("keyword5") = True Then
' THE CODE TO COPY TO FILE
End If
Next Helement
Does anything exist that would work like:
If Helement.InnerHtml.Contains("keyword1", "keyword2", "keyword3", "keyword4", "keyword5")
The way I'm doing it now just seems wasteful, and I'm pretty OCD about it.
1) One approach would be to match the InnerHtml string against a regular expression containing the keywords as a list of alternatives:
Imports System.Text.RegularExpressions
Dim keywords As New Regex("keyword1|keyword2|keyword3")
...
If keywords.IsMatch(HElement.InnerHtml) Then ...
This should work well if you know all your keywords beforehand.
2) An alternative approach would be to build a list of your keywords and then compare the InnerHtml string against each of the list's elements:
Dim keywords = {"keyword1", "keyword2", "keyword3"}
...
For Each keyword As String In keywords
If HElement.InnerHtml.Contains(keyword) Then ...
Next
Edit: The extension method suggested by Rob would result in more elegant code than the above approach #2, IMO.
You could write an Extension Method to string that provides a multi-input option, such as:
Public Module StringExtensionMethods
Private Sub New()
End Sub
<System.Runtime.CompilerServices.Extension> _
Public Function Contains(ByVal str As String, ByVal ParamArray values As String()) As Boolean
For Each value In values
If str.Contains(value) Then
Return True
End If
Next
Return False
End Function
End Module
You could then call that instead, as in your second example :)
Here's another extension method that cleans up the logic a little with LINQ:
<Extension()>
Public Function MultiContains(str As String, ParamArray values() As String) As Boolean
Return values.Any(Function(val) str.Contains(val))
End Function

How do I append a 'where' clause using VB.NET and LINQ?

I am pretty new to VB.NET and am having a bit of trouble here with something I thought should be simple.
Keeping it simple, let's say I have a Document table with "Name" that I want to search on (in reality there are several other tables, joins, etc. ..). I need to be able to build the query using a where clause based on string values passed in.
Example - the user may pass in "ABC", "ABC DEF", "ABC DEF GHI".
The final query would be (the syntax is not correct, I know):
Select * from Documents Where Name Like %ABC% AND Name Like %DEF% AND Name like %GHI%
So, I thought I could do something like this.
Dim query = From document In _context.Documents
<< loop based on number of strings passed in >>
query = query.Where( ... what goes here?? )
For some reason, being brain-dead or something, I can't figure out how to make this work in VB.NET, or if I'm doing it correctly.
I believe this is how you would do it in VB (I'm a C# developer):
query = query.Where(Function(s) s = "ABC")
See LINQ - Sample Queries for some examples.
I think the tricky part here is the unknown number of query parameters. You can use the underlying LINQ IQueryable(Of T) here to help.
I think the following would work (it's not compiled, just notepad code here):
Public Function GetDocuments(criteria as String)
Dim splitCriteria = SplitTheCriteria(criteria)
dim query = from document in _context.Documents
For Each item in splitCriteria
Dim localItem = item
query = AddCriteriaToQuery(query, localItem)
Next
dim matchingDocuments = query.ToList()
End Function
Private Function AddCriteriaToQuery(query as IQueryable(Of Document), criteria as string) as IQueryable(Of Document)
return query.Where(Function(doc) doc.Name = criteria)
End Function
Since LINQ will delay-execute the query you can append where clauses onto your query in the loop and then call .ToList() at the end to execute the query.
In LINQ to SQL you can add WHERE clauses to your query using the .Where method of the query object, as you noted in your question. To use the LIKE operator, try using the .Contains method of the object you're querying in the Lambda expression of your call to the Where method.
Here's a simplified example in a console application. Hopefully it will lead you in the correct direction.
Public Class Doc
Private _docName As String
Public Property DocName() As String
Get
Return _docName
End Get
Set(ByVal value As String)
_docName = value
End Set
End Property
Public Sub New(ByVal newDocName As String)
_docName = newDocName
End Sub
End Class
Sub Main()
Dim Documents As New List(Of Doc)
Documents.Add(New Doc("ABC"))
Documents.Add(New Doc("DEF"))
Documents.Add(New Doc("GHI"))
Documents.Add(New Doc("ABC DEF"))
Documents.Add(New Doc("DEF GHI"))
Documents.Add(New Doc("GHI LMN"))
Dim qry = From docs In Documents
qry = qry.Where(Function(d) d.DocName.Contains("GHI"))
Dim qryResults As List(Of Doc) = qry.ToList()
For Each d As Doc In qryResults
Console.WriteLine(d.DocName)
Next
End Sub
Note the .Contains("GHI") call in the Lambda expression of the .Where method. I'm referencing the parameter of the expression, "d", which exposes the DocName property, which further exposes the .Contains method. This should produce the LIKE query you're expecting.
This method is additive, i.e. the call to the .Where method could be enclosed in a loop to make additional LIKE operators added to the WHERE clause of your query.
Dim query = From document In _context.Documents where document.name = 'xpto' select document
Or
Dim query = From document In _context.Documents where document.name.contains('xpto') select document
If you do this in a loop, you can do something like this:
.Where(Function(i as mytype) i.myfiltervar = WhatIWantToSelect)