How to mock a method (custom behavior) with Rhino Mocks in VB.NET - vb.net

How can I mock one method with RhinoMocks in VB.Net? The reference I found is in C#:
Expect.Call(delegate{list.Add(0);}).IgnoreArguments()
.Do((Action<int>)delegate(int item) {
if (item < 0) throw new ArgumentOutOfRangeException();
});
SharpDevelop converts this to:
Expect.Call(Function() Do
list.Add(0)
End Function).IgnoreArguments().Do(DirectCast(Function(item As Integer) Do
If item < 0 Then
Throw New ArgumentOutOfRangeException()
End If
End Function, Action(Of Integer)))
But that doesn't work either (it doesn't compile).
This is what I want to do: create a new object and call a method which sets some properties of that method. In real-life this method, will populate the properties with values found in the database. In test, I would like to mock this method with a custom method/delegate so that I can set the properties myself (without going to the database).
In pseudo-code, this is what I'm trying to do:
Dim _lookup As LookUp = MockRepository.GenerateMock(Of LookUp)()
_luvalue.Expect(Function(l As LookUp) l.GetLookUpByName("test")).Do(Function(l As LookUp) l.Property = "value")

Unfortunately you're attempting to do both a Sub lambda and a Statement Lambda. Neither are supported in VS2008 (but will be in the upcoming version of VS). Here is the expanded version that will work for VB
I'm guessing at the type of m_list
Class MockHelper
Dim m_list as new List(Of Object)
Public Sub New()
Expect(AddressOf CallHelper).IgnoreArguments().Do(AddressOf Do Hepler)
End Sub
Private Sub CallHelper()
m_list.Add(0)
End Sub
Private Sub DoHelper(ByVal item as Integer)
if item < 0 Then
Throw New ArgumentOutOfRangeException
End If
End Sub
End Class

I have never mocked something w/ both a delegate and a lambda so I can't give a full solution to this problem, but I did want to share some example code for the usual "AssertWasCalled" function in Rhino Mocks 3.5 for vb developers because I spent some time trying to grok this... (keep in mind the below is kept simple for brevity)
This is the method under test - might be found inside a service class for the user object
Public Sub DeleteUserByID(ByVal id As Integer) Implements Interfaces.IUserService.DeleteUserByID
mRepository.DeleteUserByID(id)
End Sub
This is the interactive test to assert the repository method gets called
<TestMethod()> _
Public Sub Should_Call_Into_Repository_For_DeleteProjectById()
Dim Repository As IUserRepository = MockRepository.GenerateStub(Of IUserRepository)()
Dim Service As IUserService = New UserService(Repository)
Service.DeleteUserByID(Nothing)
Repository.AssertWasCalled(Function(x) Wrap_DeleteUserByID(x))
End Sub
This is the wrap function used to ensure this works w/ vb
Function Wrap_DeleteUserByID(ByVal Repository As IUserRepository) As Object
Repository.DeleteUserByID(Nothing)
Return Nothing
End Function
I found this to be a very nasty solution, but if it helps someone w/ the same issues I had it was worth the time it took to post this ;)

Related

ByRef Local Variable using Linq

I'm having trouble with selecting only part of a collection and passing it by reference.
So I have a custom class EntityCollection which is , who guessed, a collection of entities. I have to send these entities over HTTPSOAP to a webservice.
Sadly my collection is really big, let's say 10000000 entities, which throws me an HTTP error telling me that my request contains too much data.
The method I am sending it to takes a Reference of the collection so it can further complete the missing information that is autogenerated upon creation of an entity.
My initial solution:
For i As Integer = 0 To ecCreate.Count - 1 Step batchsize
Dim batch As EntityCollection = ecCreate.ToList().GetRange(i, Math.Min(batchsize, ecCreate.Count - i)).ToEntityCollection()
Q.Log.Write(SysEnums.LogLevelEnum.LogInformation, "SYNC KLA", "Creating " & String.Join(", ", batch.Select(Of String)(Function(e) e("nr_inca")).ToArray()))
Client.CreateMultiple(batch)
Next
ecCreate being an EntityCollection.
What I forgot was that using ToList() and ToEntityCollection() (which I wrote) it creates a new instance...
At least ToEntityCollection() does, idk about LINQ's ToList()...
<Extension()>
Public Function ToEntityCollection(ByVal source As IEnumerable(Of Entity)) As EntityCollection
Dim ec As New EntityCollection()
'ec.EntityTypeName = source.FirstOrDefault.EntityTypeName
For Each Entity In source
ec.Add(Entity)
Next
Return ec
End Function
Now, I don't imagine my problem would be solved if I change ByVal to ByRef in ToEntityCollection(), does it?
So how would I actually pass just a part of the collection byref to that function?
Thanks
EDIT after comments:
#Tim Schmelter it is for a nightly sync operation, having multiple selects on the database is more time intensive then storing the full dataset.
#Craig Are you saying that if i just leave it as an IEnumerable it will actually work? After all i call ToArray() in the createmultiple batch anyway so that wouldn't be too much of a problem to leave out...
#NetMage you're right i forgot to put in a key part of the code, here it is:
Public Class EntityCollection
Implements IList(Of Entity)
'...
Public Sub Add(item As Entity) Implements ICollection(Of Entity).Add
If IsNothing(EntityTypeName) Then
EntityTypeName = item.EntityTypeName
End If
If EntityTypeName IsNot Nothing AndAlso item.EntityTypeName IsNot Nothing AndAlso item.EntityTypeName <> EntityTypeName Then
Throw New Exception("EntityCollection can only be of one type!")
End If
Me.intList.Add(item)
End Sub
I Think that also explains the List thing... (BTW vb or c# don't matter i can do both :p)
BUT: You got me thinking properly:
Public Sub CreateMultiple(ByRef EntityCollection As EntityCollection)
'... do stuff to EC
Try
Dim ar = EntityCollection.ToArray()
Binding.CreateMultiple(ar) 'is also byref(webservice code)
EntityCollection.Collection = ar 'reset property, see below
Catch ex As SoapException
Raise(GetCurrentMethod(), ex)
End Try
End Sub
And the evil part( at least i think it is) :
Friend Property Collection As Object
Get
Return Me.intList
End Get
Set(value As Object)
Me.Clear()
For Each e As Object In value
Me.Add(New Entity(e))
Next
End Set
End Property
Now, i would still think this would work, since in my test if i don't use Linq or ToEntityCollection the byref stuff works perfectly fine. It is just when i do the batch thing, then it doesn't... I was guessing it could maybe have to do with me storing it in a local variable?
Thanks already for your time!
Anton
The problem was that i was replacing the references of Entity in my local batch, instead of in my big collection... I solved it by replacing the part of the collection that i sent as a batch with the batch itself, since ToList() and ToEntityCollection both create a new object with the same reference values...
Thanks for putting me in the correct direction guys!

Why does Intellisense not work for Default Public Property (sometimes)?

I've had problems with default properties in my code that I can - at least to some extent - reproduce with the following code:
Public Class Dawg
Public Enum DawgEnum
Demo1
Demo2
End Enum
Default Public Property DawgProp(x As DawgEnum) As String
Get
Return "whatever"
End Get
Set(value As String)
'...
End Set
End Property
Public Sub DawgSub()
Me(DawgEnum.Demo1) = "Intellisense works here..."
End Sub
End Class
Public Class DawgTest
Public Sub SomeSub()
Dim d As New Dawg
d(Dawg.DawgEnum.Demo1) = "And here as well..."
End Sub
Public Shared Sub AnotherSub()
Dim d As New Dawg
d(Dawg.DawgEnum.Demo1) = "No help for finding 'Dawg.DawgEnum.Demo1' here..."
d.DawgProp(Dawg.DawgEnum.Demo1) = "...Intellisense helped here..."
End Sub
End Class
The problem being that within the "Shared Sub AnotherSub" when I start typing "d(" Intellisense does not offer me the list of available Enum values for the property.
I also have the problem in other classes that structurally are identical to the class "DawgTest" (a simple public class) where it doesn't work either (within a shared sub or not) - although there are no problems in the test above.
Of course, this isn't a major problem since the code compiles fine and I can always use the explicit property, but it's very annoying and confusing and just like to know, if this is by-design? And, if so: why? If not: how can I fix it?
I can't for sure say, if this problem has been there all along or, if it just came up out of nowhere...
Any thoughts - even 5 cents or less - on the issue would be greatly appreciated :o)

Error capturing onreadystatechange of MSXML2.DOMDocument in VBA

I am getting an error trying to arrange asynchronous loading and parsing of an XML document in VBA using a wrapper class.
Following the ideas described in this msdn article and this tutorial which have worked perfectly for asynchronous handling of MSXML2.XMLHTTP40.send method I attempted to do a similar thing for DOMDocument.loadXML.
Here is the code from the wrapper class DOMMonitor
Private domDoc As MSXML2.DOMDocument
Public Event onXmlLoadComplete(d As MSXML2.DOMDocument)
Public Sub loadXML(XmlFilePath As String)
Set domDoc = CreateObject("MSXML2.DOMDocument")
domDoc.async = True
domDoc.onreadystatechange = Me ' error occurs here
domDoc.Load XmlFilePath
End Sub
Public Sub onLoadComplete()
If domDoc.readyState = "4" Then
RaiseEvent onXmlLoadComplete(domDoc)
End If
End Sub
I have made onLoadComplete the default method by setting VB_UserMemId = 0, so it is supposed to be invoked when domDoc fires onreadystatechange .
However when I invoke loadXML
Dim dm As DomMonitor
Set dm = New DomMonitor
dm.loadXML txtXMLData
i get the following runtime error in this line:
domDoc.onreadystatechange = Me
This object cannot sink the 'onreadystatechange' event. An error occurred marshalling the object's IDispatch interface
What am I doing wrong and is there a good workaround here?
Thanks in advance.
P.S. The reason I am republishing the event is that I do not necessarily want use the default method of the final subscriber for this purpose. However, as things stand now I do not even get to that stage.
The way I read that msdn article is that to assign a wrapper class to the readystatechange, the object has to be either an IXMLHTTPRequest or an IServerXMLHTTPRequest object (bullet 3). Since your object is a DOMDocument, readystatechange doesn't accept an object.
However, you can instantiate a DOMDocument WithEvents (bullet 2), making the other way redundant, I guess. I don't have an xml file large enough to test, but I think this should work. I assume that if the class loses scope, all bets are off, so I made it a global variable.
In a standard module
Public clsDOMMonitor As CDOMMonitor
Sub test()
Set clsDOMMonitor = New CDOMMonitor
clsDOMMonitor.loadXML "C:\Users\dkusleika\Downloads\wurfl-2.3.xml"
End Sub
In CDOMMonitor class
Private WithEvents mDoc As MSXML2.DOMDocument
Private Sub mDoc_onreadystatechange()
If mDoc.readyState = 4 Then
MsgBox "second"
End If
End Sub
Public Sub loadXML(XmlFilePath As String)
Set mDoc = New MSXML2.DOMDocument
mDoc.async = True
mDoc.Load XmlFilePath
MsgBox "first"
End Sub
I assume that setting async to True is all that is needed for this to work properly. My 100k xml file is probably done so fast that that the event never gives up control. But if you had a sufficiently large xml file, I think you would get "first" before "second".
Change the class' Instancing property from Private to PublicNotCreatable when late binding, whilst also applying the tweak which you have mentioned.
Use the above example when early binding (as in your case).

Assigning a reference type object a value on a secondary thread, and then working with this object from my primary thread

I am trying to create a variable which is of type MyReferenceTypeObject and of value null on thread one, use a delegate to make this thread equal to a new instance of MyReferenceTypeObject on thread two, and then access members of MyReferenceTypeObject back on thread one (in the delegates callback method).
My code is below:
Module Module1
Delegate Sub EditReferenceTypePropertyDelegate(ByVal referenceTypeObject As MyReferenceTypeObject, ByVal numberToChangeTo As Integer)
Sub Main()
Dim editReferenceDelegate = New EditReferenceTypePropertyDelegate(AddressOf EditReferenceTypeProperty)
Dim newReferenceTypeObject As MyReferenceTypeObject
editReferenceDelegate.BeginInvoke(newReferenceTypeObject, 2, AddressOf EditReferenceCallback, newReferenceTypeObject)
Console.ReadLine()
End Sub
Sub EditReferenceTypeProperty(ByVal referenceTypeObject As MyReferenceTypeObject, ByVal numberToChangeTo As Integer)
referenceTypeObject = New MyReferenceTypeObject()
referenceTypeObject.i = numberToChangeTo
End Sub
Sub EditReferenceCallback(ByVal e As IAsyncResult)
Dim referenceObject = DirectCast(e.AsyncState, MyReferenceTypeObject)
Console.WriteLine(referenceObject)
End Sub
End Module
Class MyReferenceTypeObject
Public Property i As Integer
End Class
However, newReferenceTypeObject comes into my callback method as null. I think I understand why, but the problem is that I need to pull some data from a database which I then need to pass into the constructor of newReferenceTypeObject, this takes a couple of seconds, and I don't want to lock up my UI while this is happening. I want to create a field of type MyReferenceTypeObject on thread one, instantiate this on thread two (after I have pulled the data of the database to pass into the constructor) and then work with members of the object back on thread one once the instantiation is complete.
Is this possible? I am using VB.Net with .Net 4.0 on Visual Studio 2012.
If you want to keep the GUI responsive during a long running action, I'd consider using the Task<> library (Comes with .NET 4.0). Here's a quick example.
Sub Main()
Dim instantiateTask = New Task(Of MyReferenceTypeObject)(Function()
' Call your database to pull the instantiation data.
Return New MyReferenceTypeObject With {.i = 2}
End Function)
instantiateTask.Start() ' Start the task -> invokes a ThreadPool.Thread to do the work.
instantiateTask.ContinueWith(Sub(x)
Console.WriteLine(x.Result.I)
End Sub, TaskScheduler.FromCurrentSynchronizationContext())
End Sub
.Wait blocks the GUI thread. However, you could use ContinueWith which is async and therefor nonblocking. Also you need to provide the TaskScheduler ( TaskScheduler.FromCurrentSynchronizationContext ) from the GUI thread to prevent cross-thread exceptions in case you want to update the UI from within the async method.

Dynamic properties for classes in visual basic

I am a vb.net newbie, so please bear with me. Is it possible to create properties (or attributes) for a class in visual basic (I am using Visual Basic 2005) ? All web searches for metaprogramming led me nowhere. Here is an example to clarify what I mean.
public class GenericProps
public sub new()
' ???
end sub
public sub addProp(byval propname as string)
' ???
end sub
end class
sub main()
dim gp as GenericProps = New GenericProps()
gp.addProp("foo")
gp.foo = "Bar" ' we can assume the type of the property as string for now
console.writeln("New property = " & gp.foo)
end sub
So is it possible to define the function addProp ?
Thanks!
Amit
It's not possible to modify a class at runtime with new properties1. VB.Net is a static language in the sense that it cannot modify it's defined classes at runtime. You can simulate what you're looking for though with a property bag.
Class Foo
Private _map as New Dictionary(Of String, Object)
Public Sub AddProperty(name as String, value as Object)
_map(name) = value
End Sub
Public Function GetProperty(name as String) as Object
return _map(name)
End Function
End Class
It doesn't allow direct access in the form of myFoo.Bar but you can call myFoo.GetProperty("Bar").
1 I believe it may be possible with the profiling APIs but it's likely not what you're looking for.
Came across this wondering the same thing for Visual Basic 2008.
The property bag will do me for now until I can migrate to Visual Basic 2010:
http://blogs.msdn.com/vbteam/archive/2010/01/20/fun-with-dynamic-objects-doug-rothaus.aspx
No - that's not possible. You'd need a Ruby like "method_missing" to handle the unknown .Foo call. I believe C# 4 promises to offer something along these lines.