I want to host WCF service inside unit Test using ServiceHost.
And then running them in on a build machine.
Is it possible to host web services like this ? if yes how it's done ?
Thanks
Sure, you just use a ServiceHost like you would if it was a self-hosted wcf service. I typically do this in a test fixture/class setup/teardown.
For example with NUnit:
[TestFixture]
public class MyTests
{
private ServiceHost service;
[TestFixtureSetUp]
public void FixtureSetUp()
{
service = new ServiceHost(typeof(MyService));
service.Open();
}
[Test]
public void ThisIsATest()
{
using (var client = new MyServiceClient())
client.DoStuff(); // test whatever
}
[TestFixtureTearDown]
public void FixtureTearDown()
{
if (service != null)
{
if (service.State == CommunicationState.Opened)
service.Close();
else if (service.State == CommunicationState.Faulted)
service.Abort();
}
}
}
You will also need to copy any WCF XML configuration into the test assembly's app.config.
One thing to note however is the threading model of your unit test framework. If it is possible for your test runner to run multiple test classes simultaneously, then you might try to open the same service on the same port more than once, and it might fail.
On a final note, I would typically separate tests that do and don't fire up the actual WCF service. Put tests that don't hit WCF into a separate "Unit Tests" assembly, that runs without dependencies and runs fast, and put ones that do use WCF into an "Integration Tests" assembly instead. That is just a recommendation of course, not a rule.
It is indeed possible and actually is the same as hosting a WCf service in a console application!
Add your app.config to the UT assembly and proceed as always.
(You can also configure programatically of course).
Similarly, you create a client for the host in code.
Instantiate and open your host and client on the set-up method, you can do this per test or per class, depending on the level of isolation you seek.
So you see, you don't have to have a separate deployment stage.
Having said all the above, I would actually discourage you from working with services in your Unit tests. One of the best practices concerning unit tests is that they should be very fast to run. Making calls to web services (even local) is very costly in UT.
Moreover, this kind of contradicts the semantics of a UNIT test. You should be testing small bits of functionality. And strive to mock out external dependencies such as Database access, External services and even other classes. I would be happy to elaborate and refer to more information if you feel like it.
I think the problem here is that UnitTests are usually just a set of binaries that are executed by some other framework (NUnit, xUnit,...).
You can try on the TestSuite setup start another Thread and run WCF on that Thread, but I believe it is much more involved into this approach and Test suites may no like it as well.
What I would do is to setup a deployment step on your build machine of the WCF service to IIS or redeployment to the Windows Service hosted WCF before running UnitTests.
Related
I've been trying to look for examples on the internet or anywhere of how to implement gRPC unit testing in c# but can't for the love of me find anything, or I could just be over seeing things.
If someone could point me in the right direction I would be very grateful.
S
For simple unary services, as long as you're not doing anything interesting with gRPC headers, cancellation, etc - you should be able to treat your server type just as the service type - create an instance, call the simple unary method(s), check the results. However, this won't validate marshalling or the gRPC layer, etc. For that, you really need an integration test. Likewise, anything involving streaming probably needs an integration test, because there's a lot of background plumbing. If this was me, I would simply create a server as a test-fixture, and write my tests by creating a genuine client that can talk to those services.
If you're using the Google server code, something like the fixture I'm using here should do (don't worry about the AddCodeFirst - that's some protobuf-net.Grpc additions; just use the same registration code that you would have used in the real server). If you need to test with the Microsoft server code, you'd need to host Kestrel in process as the fixture; but fortunately: they're mostly completely interchangeable, so if in doubt: use whichever is simpler. Note that on the client side: again, since the idea of gRPC is to be transparently interoperable between languages/runtimes/frameworks, it shouldn't matter whether the client uses the Microsoft or Google transport. Historically there have been some minor differences, but these are a: pretty niche, and b: get fixed when found.
I usually do something like this
1: I create an interface for the service to test the DI system and or to be able to mock it. I do this as registering the gRPC service with the framework kinde-does-that as well as it's created similar to a controller
Then I write my Test like so:
[TestMethod]
public async Task Call_Grpc_Method_Test()
{
var grpcService = _service.GetRequiredService<IMyService>();
var request = new MyRequest {When=DateTime.Now.AddDays(30)};
var context = TestServerCallContext.Create(
method: nameof(IMyService.WinningLotteryNumbers)
, host: "localhost"
, deadline: DateTime.Now.AddMinutes(30)
, requestHeaders: new Metadata()
, cancellationToken: CancellationToken.None
, peer: "10.0.0.25:5001"
, authContext: null
, contextPropagationToken: null
, writeHeadersFunc: (metadata) => Task.CompletedTask
, writeOptionsGetter: () => new WriteOptions()
, writeOptionsSetter: (writeOptions) => { }
) ;
var answer= await grpcService.WinningLotteryNumbers(request, context);
Assert.IsNotNull(answer);
}
IMyService is the gRPC service I want to test and I just create an IServiceProvider that can build one for me via the actual implementations making it an integration test or via mocked objects making it a unit test.
The TestServerCallContext class is located in the NuGet package Grpc.Core.Testing, you would need to add that to your test project.
as to the constructor, well you can use hard coded sample data as I demonstrated or go fancy and add meta data for headers and authcontex as well as the stuff when you need it...
As you can see, not that hard to do once you get started ;-)
I'm trying to test my WCF Service Endpoints using IntelliTest I've created the IntelliTest units in a test project, but I need to specify the Server address and port in order for the test to remotely even to work.
This is one of the PexMethods
public ICredentials CredentialsTest()
{
ICredentials result = Service.Credentials();
return result;
// TODO: add assertions to method ServiceTest.CredentialsTest()
}
I've added
Service.ServiceEndPointAddress = "net.tcp://localhost:51010/WCFService";
but when I run the test I get an InvalidProgramException.
What I would like to do is assign the ServiceEndPointAddress once as it is a static property in Service. Anybody that can give some guidance?
There are a couple of issues here:
Getting an InvalidProgramException indicates that IntelliTest has instrumented your code in such a manner as to cause the underlying CLR to raise the exception. That would be a bug in IntelliTest. I request you to file this using the Send a Smile in Visual Studio.
You should not use IntelliTest to directly explore your WCF code. Instead, you should isolate the WCF related external dependencies using mocks (much as you would do if you were writing unit tests by hand), and then run IntelliTest to explore the resultant code. For mocking you can consider using Fakes.
I have a Windows Service that hosts a WCF service and I am successfully able to connect to it using WCFTestClient and a Custom Client. The windows service is based upon what used to be an exe, but since the program will be used for a long running process on a server, the service is a better route. The problem is that I cannot access static variables in the application from the WCF service.
In the .exe (I switched this to a .dll which is the server application) I use a global class implemented as such:
public static class Globals
{
....
}
This holds references to the major parts of the program so that if any part needs to reference another I can use the syntax Globals.JobManager.RunJob().
The problem that I am encountering is that the WCF service is not able to reference Globals at run-time. One example of where I need this to be done is in the GetJob method:
public class ConsoleConnection : IConsoleConnection
{
public string[] RetrieveJobList()
{
string[] jobs = Globals.JobManager.GetAllJobNames().ToArray();
return jobs;
}
}
This method returns null when tested in WCFTestClient and throws an exception in the created client.
I believe this issue to be caused by the way the Windows Service, WCF Service, and the application DLL are initiated. The current method is such:
public class ETLWindowsService : ServiceBase
{
....
protected override void OnStart(string[] args)
{
if (serviceHost != null)
{
serviceHost.Close();
}
Globals.InitializeGlobals();
serviceHost = new ServiceHost(typeof(ConsoleConnection));
serviceHost.Open();
}
....
}
Here the Windows Service starts, Calls the Globals.InitializeGlobals() that creates all the necessary parts of the application, then starts the WCF service (If this is the wrong way to do this, let me know. I'm piecing this together as I go). I'm assuming that these actions are being done in the wrong order and that is the cause of the problems.
Do I need to have the Windows Service create the WCF Service which in turn creates the application (this doesnt make sense to me), or do I have the Windows Service create the application which then creates the WCF Service? Or is there a third option that I am missing?
The application is in a .dll with the WCF in a separate .dll
I totally agree with Andy H.
If I review this kind of code, I won't try to make the stuff work with the global static variable (even if in the end this is probably possible). A static global class is smelly. First of all, I will figure out to make it work without it.
There are several solution: dependency injection, messaging communication, event driven...
To help you: a long running process in a web service is very common, youy have a good description
here. But in any case, it never uses a static class to synchronize the jobs :)
Improve your design, and you will see that your current problem won't exist at all.
I need to change my unit test from local to remote tests and so far I thought that all I had to do is change UrlToTest to point to another server... But VS keeps on insisting to create a Development Web Server instead of using the one that is already running.
So after reading some docs my question actually is do I have install Test Controller and Test Agent on both remote and local computer or what? What if the WebService is on Linux...
Note that I don't want to debug the application that I'm testing. I simply want tests to be executed for a WebService that is already running, that is deployed.
I probably should mention that all my tests consists of WebService calls and some checks like this:
[TestMethod()]
[HostType("ASP.NET")]
[AspNetDevelopmentServerHost("MainProjectName", "/")]
[UrlToTest("http://servername:port/websitename/TestingOnlyWebForm.aspx")]
public void LoginEmptyDataTest()
{
IUserService userService = CreateIUserService();
string email = "";
string password = "";
ReturnMessage<User> actual;
actual = userService.Login(email, password);
Assert.AreNotEqual(true, actual.Status);
Assert.AreNotEqual("db_error", actual.Info);
}
But I have also more complicated tests in which I change some data and send it to another WebService and so on.
Note that UrlToTest previously was pointing to localhost at which point it works but starts a developer server which is not what I want.
What you are trying to is not possible. All that generated unit test is trying to do is to run the test locally on the machine either using the development server by specifying AspNetDevelopmentServerHost or by using local IIS, when AspNetDevelopmentServerHost is not present.
If you want to test remote services right click your unit test project and add a service reference. Point to your service give it a namespace, say Services, and generate the proxies. Once you have the proxies generated just instantiate them and call the methods. Also remove all the unneeded attributes from your test. Your test should roughly look like this:
[TestMethod]
public void LoginEmptyDataTest()
{
using (var userServiceClient = new Services.UserServiceClient(
"BasicHttpBinding_IUserService",
"http://someremotehost/userservice.svc"))
{
var result = userServiceClient.Login("user", "password");
// asserts go here
}
}
This may solve your immediate problem however you should re-think what you are doing as #eglasius said. what happens if the code you call changes state internally? Next test might fail because of that so you need clean-up strategies otherwise your tests will be very brittle and you'll end up ignoring them.
Update: passing an address at run-time. Change the first parameter to whatever enpoint configuration name you have in your config file.
I'll take a stab in the dark at this one because I did something similar recently.
In my case my test project referenced the service project to provide visibility of the service and data contracts the Web Service implements and consumes.
To resolve this - though it can be ignored - move the contracts to a new project. Then have the service and test projects reference the new project, and remove the test projects' reference to the service.
Hope that makes sense!
Once the reference is removed, VS will no longer feel the need to start up your service when you run your tests.
You can disable the startup of the Service Host in the Project settings of your WCF Service Project. Right Click - WCF Options - Uncheck "Start WCF Service Host when debugging another project in the same solution".
You really have to consider the nature of what you're trying to achieve here.
It's hard to tell exactly what you're hitting of the code. I have the impression, you have is a website that calls a web service. You're testing the client code in that context, instead of just testing the service.
If that's the case, remove the attributes and point the url to the correct service like UrbaEsc guided you to. If you don't remove the attributes, you're running the calling code in the context of the site.
Even if the above is not the scenario, and based on what you replied to UrbanEsc in the comments, you'd then be testing an external call to the webservice initiated from the same site process.
You said:
"Found it, but VS still starts something on localhost and then trys to reach external server... Seems like VS is just not designed for real remote testing of webservices"
Read the above. I'd say you need to better understand what you're enabling. You can certainly test remote web services, like you can do pretty much anything you can code. You do that from client code that knows nothing different that what any other external client of the service would know. Support doesn't stop there, as you can do load testing of the service.
Note that what you're doing aren't unit tests, these are integration tests. Or depending on the scope of your system, full system tests.
I have a WCF service that is hosted on a c# console application. Is there a way to restart this service, preferably by calling an endpoint in the service itself (ex. myService.Restart()).
Thanks
I have to do something similar when I perform an automatic update of a remote WCF service. In your Restart() method, close the host:
try
{
host.Description.Endpoints.Where(x => !x.Address.ToString().EndsWith("MEX")).ForEach(endpoint => _log.InfoFormat("Closing {0}", endpoint.Address));
host.Close(TimeSpan.FromSeconds(5));
}
catch (Exception)
{
host.Abort();
}
I wait for my update to apply, and then after a success or failure, I re-open the host using the same code I used to start it in the first place.
If you just wanted to restart immediately, you could just call host.Open(), or you could set up a timer to call it, etc.
try
{
host.Open();
host.Description.Endpoints.Where(x => !x.Address.ToString().EndsWith("MEX")).ForEach(endpoint => _log.InfoFormat("Host opened at: {0}", endpoint.Address));
}
catch (Exception ex)
{
_log.Error("Unable to open host.", ex);
}
To answer my question, I have solved the problem by doing the following:
Separating the code that loads the DLL files from the WCF service code into another class library project
Create an interface with the same method signatures as the ones that load DLL files in the new project (this interface is used by both projects now)
In the web service, load the other project in a new application domain. This way the DLL files are locked by the new application domain not the default.
If I want to update my nunit DLL files now, all I have to do is unload the application domain from the web service, update the files and finally create a new application domain.
AppDomain remoteDomain = AppDomain.CreateDomain("New Domain");
IClass1 class1 = (IClass1)remoteDomain.CreateInstanceFromAndUnwrap(
"Test1.dll", "Test1.Class1");
Note: IClass1 is the common interface between the projects.
you definitely are not going to be able to 'restart' a faulted service from calling that same service itself. In theory you could host 2 services in the same process. put the one you want to be 'restartable' in a public static variable and restart it within the other service. The problem would be restarting the restarter service if it faults... :) and you definitely want 'administrator-like' restrictions on your restarter service so unauthorized users can't do it.
It's a bit kludgy, but I suppose you could expose a callback on your service that the host could attach to and take appropriate action when it's triggered. That would give your host the ability to decide what a "restart" really means and how it needs to be executed. More importantly, it lets your decide whether it should do something extreme like spawn off a watcher process and then off itself or gracefully trash and reinstantiate your service (preferable).
Mmmmmm... kludge....
You cannot ask a service to restart itself. Consider a windows service (a service hosted in windows provided container) which has a RESTART functionality. Here RESTART functionality is provided not by the service but by the container. The container controls how to stop the service and start it.
Similarly in your case, you should try to look out for options if your container can provide the functionality you need. Since you want to control it remotely, the container should also be available remotely, which cannot be possible if the container is a console application. Instead it has to be another web service or web application.