WCF Service Method - Refactoring for Unit Test and Mocking - wcf

I've a WCF service with the following method:
Public Function ScheduleEmail(ByVal request As ScheduleEmailRequest) As ScheduleEmailResponse _
Implements EmailProtocol.ISchedulingService.ScheduleEmail
Try
If Not Email.IsValidEmailAddress(request.EmailAddress) Then
EmailSchedulerTrace.Source.WriteError(String.Format("Email with template '{0}' was not sent to '{1}' because it the address is invalid.", request.EmailName, request.EmailAddress))
Else
Dim mgr As New JobManager
Dim job As New EmailJob
Dim suppression As New SuppressionManager
Dim emailItem As Email = Email.GetEmailByName(request.EmailName)
If suppression.CheckSuppresion(emailItem, request.EmailAddress) Then
job.JobGuid = Guid.NewGuid
job.EmailAddress = request.EmailAddress
job.EmailGuid = emailItem.ID
job.ScheduledSendTime = request.ScheduledTime
job.CustomAttributes = request.CustomAttributes
job.ConsumerID = Email.GetConsumerId(request.CustomAttributes)
mgr.ScheduleJob(job)
Else
EmailSchedulerTrace.Source.WriteWarning(String.Format("Email with template '{0}' was not sent to '{1}' because it was suppressed.", request.EmailName, request.EmailAddress))
End If
End If
Catch ex As Exception
EmailSchedulerTrace.Source.WriteError(ex)
Throw
End Try
Return New ScheduleEmailResponse
End Function
I need to write Unit Test for this Method. Please help me out with
Do i need to change anything in my method?
What should I mock?
Your help is greatly appreciated. Thanks in advance.
Regards,
Sachin

You need to be able to swap out any 'services' (classes that you new up in a method or fields in the class) which connect to other systems (database, email server etc) so you need to create interfaces for the classes and inject the correct implementation at runtime and in your unit test, you can create mock or fake implementations for testing purposes.
A good start would be to define an interface for:
JobManager
EmailSchedulerTrace
SuppressionManager
You also might need to move the functionality of your static methods on Email
GetEmailByName
GetConsumerId
if they encapsulate database access or any other service which you cannot isolate.

Related

.NET Web API, Return Data To Client, Then Process Transaction?

I have a VB.NET Web API controller, that performs two functions: when a request comes in, it processes the request, and then returns data to the client. The client service consuming the process has a timeout of 1.5s, in most cases my controller responds in time, however under some circumstances the controller does not respond in time.
According to telemetry the heavy lifting that will cause it to take to long to return can actually be processed after i return to the client, I've tried to modify the controller, but this has not improved the response time:
Public Class SMSController
Inherits TwilioController
<HttpPost>
Public Async Function Index() As Threading.Tasks.Task(Of ActionResult)
Dim tml = New MessagingResponse()
If String.IsNullOrEmpty(Request.Form("From")) Then Return TwiML(tml.Message(Constants.smsError))
Dim smsFrom As String = Replace(Request.Form("From"), "+1", "")
Dim db As New DB_ENTITY
Dim r = (From p In db.Task Where p.smsFrom = smsFrom).FirstOrDefault()
If IsNothing(r) Then
Try
Return TwiML(tml.Message(Constants.smsError))
Finally
HeavyLifting()
End Try
End If
'// Code shortened for brevity
It is my understanding that this should return the twilio markup to the client, then run the heavylifting() but this does not seem to be the case.
The heavy stuff returns in time the majority of the time, so i don't want to task it and process it later, is there a better way i can return the markup to the client and then perform the processing?

Deserialize object content manually in Web-API OData

When using WCF Data Services Client to communicate with my OData API, I occasionally need to use the
AddRelatedObject method of System.Data.Services.Client.DataServiceContext
This generates a POST to the endpoint that looks like:
http://base_url/odata/Entity(key)/NavigationProperty
The content of the http request is the serialized NavigationProperty entity.
Since a post to this URL is unmapped in a default odata controller, I wrote a new EntitySetRoutingConvention class:
Public Class AddRelatedObjectRoutingConvention
Inherits EntitySetRoutingConvention
Public Overrides Function SelectAction(odataPath As Http.OData.Routing.ODataPath, controllerContext As Http.Controllers.HttpControllerContext, actionMap As ILookup(Of String, Http.Controllers.HttpActionDescriptor)) As String
If controllerContext.Request.Method = Net.Http.HttpMethod.Post Then
If odataPath.PathTemplate = "~/entityset/key/navigation" Then
If actionMap.Contains("AddRelation") Then
Return "AddRelation"
End If
End If
End If
Return Nothing
End Function
End Class
I added this to the default routing conventions list and passed it to the MapODATARoute routine.
In my base controller I implemented AddRelation, and it is called perfectly.
From this I am able to get the odatapath, and parse it to determine the types and keys that are needed.
The problem I am having, is that once I know that my parent entity is a Tenant with a key of 1, and that the NavigationProperty of Users in the Tenant entity is a User entity, I can not figure out how to manually call an odatamediatypeformatter to deserialize the http content into the User entity so that I can then add it to the Tenants Users collection.
I have reviewed the OData source in an effort to find out how to the Web-API pipeline calls and deserializes the entity but have been unable to find the point at which the OData library takes the http content and turns it into an entity.
If anyone has an ideas to point me in the right direction, I will continue researching and update if I figure out more.
UPDATE 06/28/2013
Thanks to RaghuRam, I've been able to get closer. The problem I'm seeing is that the request.getconfiguration().formatters, or odatamediatypeformatters.create both seem to create type specific deserializers.
When I call ReadAsAsync I receive the error:
No MediaTypeFormatter is available to read an object of type 'User' from content with media type 'application/atom+xml'.
As the post shows above, the context of the controller is a Tenant, so I believe the formatters are typed to a Tenant and so can't deserialize the User.
I tried manually creating a formatter, _childDefinition is the EDM Type Reference from the navigation property of the odatapath. In this case a User.
Dim dser As New Formatter.Deserialization.ODataEntityDeserializer(_childDefinition, New Formatter.Deserialization.DefaultODataDeserializerProvider)
Dim ser As New Formatter.Serialization.ODataEntityTypeSerializer(_childDefinition, New Formatter.Serialization.DefaultODataSerializerProvider)
Dim dprovider As New Formatter.Deserialization.DefaultODataDeserializerProvider
dprovider.SetEdmTypeDeserializer(_childDefinition, dser)
Dim sprovider As New Formatter.Serialization.DefaultODataSerializerProvider
sprovider.SetEdmTypeSerializer(_childDefinition, ser)
Dim fmt As New Formatter.ODataMediaTypeFormatter(dprovider, sprovider, New List(Of Microsoft.Data.OData.ODataPayloadKind) From {Microsoft.Data.OData.ODataPayloadKind.Entry})
fmt.SupportedEncodings.Add(System.Text.Encoding.UTF8)
fmt.SupportedEncodings.Add(System.Text.Encoding.Unicode)
fmt.SupportedMediaTypes.Add(Headers.MediaTypeHeaderValue.Parse("application/atom+xml;type=entry"))
fmt.SupportedMediaTypes.Add(New Headers.MediaTypeHeaderValue("application/atom+xml"))
I have then tried:
request.content.ReadAsAsync(of User)(new List(of odatamediatypeformatter) from {fmt})
request.content.ReadAsAsync(of User)(request.getConfiguration().formatters)
request.content.ReadAsAsync(of User)(odatamediatypeformatters.create)
All giving the same error, it seems like I'm missing something obvious.
Thanks!
Steve
Just add a parameter of type User to your AddRelation action and you should be good. Web API would automatically invoke the OData formatter to read the User from the request body and bind it to your action parameter.
You can use this helper method to read OData request body,
private static Task<T> ReadODataContentAsAsync<T>(HttpRequestMessage request)
{
var formatters =
ODataMediaTypeFormatters.Create()
.Select(formatter => formatter.GetPerRequestFormatterInstance(typeof(T), request, request.Content.Headers.ContentType));
return request.Content.ReadAsAsync<T>(formatters);
}
like this,
Customer addedCustomer = ReadODataContentAsAsync<Customer>(Request).Result;

Execute a method on an existing process instance. Vb.Net

I have a windows service that I have been writing in Vb.Net. As part of this service it calls a class that has a long running Process.
I can execute commands to this process when I want to via the ServerCommands() class within the service, however I want to call these remotely. Possibly from a website or click once WPF application.
For this I have used a simple Tcp.Ip WCF example, and have verified it as working correctly.
This called OnStart()
Private _serverCommands As ServerCommands
Protected Overrides Sub OnStart(ByVal args() As String)
' Add code here to start your service. This method should set things
' in motion so your service can do its work.
Debugger.Launch()
' Action a new implementaion of the WCF Service on localhost
_host.AddServiceEndpoint(GetType(ICommunicationService), New NetTcpBinding(), String.Format("net.tcp://127.0.0.1:{0}", AppSettings.TcpServicePort))
_host.Open()
' Start the server command
_serverCommands = New ServerCommands()
_serverCommands.StartServer()
End Sub
However... when I'm calling the service through WCF its starting a new instance of the ServerCommands() Class rather than attaching to the already running thread.
The following call
Public Function DoWork() As String Implements ICommunicationService.DoWork
Dim command As String = "say hello world"
Dim service As IMinecraftService = New MinecraftService()
service.ExecuteServerSideCommand(command)
Return "Command Executed"
End Function
Implements this on the main service.
Public Sub ExecuteServerSideCommand(command As String) Implements IMinecraftService.ExecuteServerSideCommand
If (_serverCommands IsNot Nothing) Then
_serverCommands.SendCommand(command)
End If
End Sub
It appears that in debug _serverCommands is Nothing when it should be running.
How might I go about ensuring any command I execute through WCF communicates with the running instance instead of creating a new ServerCommand() instance??
I haven't tried WCF before, so I might be hitting a dead end... however I'm sure its possible.
Thanks in advance.
I found that I was calling a new instance of the MinecraftService each time I sent a command via WCF.
As Jeff rightly said, I was not making the object shared, I was only accessing a new instance of this class.
I changed it from
From
MyMainClass
Private _serverCommands As ServerCommands
My WcfService
Dim command As String = "say hello world"
MinecraftService.ServerCommands.SendCommand(command)
To
MyMainClass
Public Shared ServerCommands As ServerCommands
My WcfService
MinecraftService.ServerCommands.SendCommand(command)

How to change web service url dynamically

I have one web service reference in my project but it has two url one is live and second one is test,how to switch between these url dynamically in vb.net
http://api.test/test/SOAP.wsdl
http://api.live/live/SOAP.wsdl
'LOGPOINT:
Call mobjLogWrite.prWriteLogEntry(clsLogWriter.enuLogEntryType.INFORMATION, ASSEMBLY_ID, "Start fnHOTELSPROSearchExecute()", "fnHOTELSPROSearchExecute")
Dim objsoap As New b2bHotelSOAPService()
Dim getres As New getAvailableHotelResponse()
QLSearchXML = xmlData
objsoap.Timeout = 20000
objsoap.Url = "http://api.live/live/SOAP.wsdl"
'objsoap.Timeout = TIMEOUT
getres = objsoap.getAvailableHotel(HOTELSPRO_APIKEY.Trim(), strDestinationId, dtmCheckIn, dtmCheckOut, strCurrencyCode, "UK", True, fngetpax(xmlData), getfilter())
Call mobjLogWrite.prWriteLogEntry(clsLogWriter.enuLogEntryType.INFORMATION, ASSEMBLY_ID, "Start DeSerializing the XML Output", "fnHOTELSPROSearchExecute")
lHOTELSPROReturn = fnCustomSerializeObject(GetType(getAvailableHotelResponse), getres)
Call mobjLogWrite.prWriteLogEntry(clsLogWriter.enuLogEntryType.INFORMATION, ASSEMBLY_ID, "End DeSerializing the XML Output", "fnHOTELSPROSearchExecute")
lTempDOM.LoadXml(lHOTELSPROReturn)
Return lTempDOM
Catch ex As Exception
Call mobjLogWrite.prWriteLogEntry(clsLogWriter.enuLogEntryType.ERROR, ASSEMBLY_ID, "Catch Block Error:" + ex.ToString(), "fnCreateHOTELSPROSearchRequest")
Finally
'LOGPOINT:
Call mobjLogWrite.prWriteLogEntry(clsLogWriter.enuLogEntryType.INFORMATION, ASSEMBLY_ID, "Response From HotelsPro--->" & lHOTELSPROReturn, "fnHOTELSPROSearchExecute")
Call mobjLogWrite.prWriteLogEntry(clsLogWriter.enuLogEntryType.INFORMATION, ASSEMBLY_ID, "END Finally Block fnHOTELSPROSearchExecute()", "fnHOTELSPROSearchExecute")
End Try
the error response is returned
"I have one web service reference in my project but it has two url one is live and second one is test,how to switch between these url dynamically in vb.net"
Dynamically based on what, exactly?
Assuming you mean based on where the app is running, i.e. Test or Live, how about:
EDIT : Just saw it was meant to be in VB.Net
Dim MyService as String
If HttpContext.Current.Server.MachineName.ToString() = "LIVESERVER" Then
MyService = "http://api.live/live/SOAP.wsdl"
Else
MyService = "http://api.live/test/SOAP.wsdl"
End If
And change
objsoap.Url = "http://api.live/live/SOAP.wsdl"
to
objsoap.Url = MyService
If your Webservice is set to Dynamic, the URL is store in the app.config settings. To make this easier to change at run time (the app.config is readonly unless run with admin privileges), go to the project Settings and change the webservice setting from application scope to user scope.
Now you can change the webservice URL at any time in code using the my.settings.yourwebserviceurl... = "newwebserviceurl"
Next time you call the webservice, it will be from the new location. However, you will need to ensure both webservice call contain an indentical, or at least compatible webservice.

How do I unit test object serialization/deserialization in VB.NET 1.1?

I am looking for example code that provides a unit test to serialize and deserialize an object from a memory stream. I have found examples using C# 2.0, however my current project uses VB.NET 1.1 (don't ask me why...), so the solution can not use generics. I am also using the NUnit framework for the unit tests.
Thanks!
This is the pattern I've settled upon:
<Test()> _
Public Sub SerializationTest()
Dim obj As New MySerializableObject()
'Perform additional construction as necessary
Dim obj2 As MySerializableObject
Dim formatter As New BinaryFormatter
Dim memoryStream As New MemoryStream()
'Run through serialization process
formatter.Serialize(memoryStream, obj)
memoryStream.Seek(0, SeekOrigin.Begin)
obj2 = DirectCast(formatter.Deserialize(memoryStream), MySerializableObject)
'Test for equality using Assert methods
Assert.AreEqual(obj.Property1, obj.Property1)
'etc...
End Sub
NUnit has built in support for this which makes it quite a bit easier:
Dim obj As New MySerializableObject()
Assert.That(obj, Is.BinarySerializable)
Or for xml:
Dim obj As New MySerializableObject()
Assert.That(obj, Is.XmlSerializable)
If all you want to do is to ensure that they are serializable then all you should have to do it to do a serialization of an object and make sure no XmlSerializationException was thrown
[Test]
public void ClassIsXmlSerializable()
{
bool exceptionWasThrown = false;
try
{
// .. serialize object
}
catch(XmlSerializationException ex)
{
exceptionWasThrown = true;
}
Asset.IsFalse(exceptionWasThrown, "An XmlSerializationException was thrown. The type xx is not xml serializable!");
}
Hmm...so you are trying to write a unit test for serialization? Or for streams? This is hopefully done by MS already...but if you don't trust or implement something on your own...you could just fill object with some data, save it, restore it, and check if the fields values are in place?