Unitesting

12
Unit testing SignalR applications You can use the unit test features in SignalR 2.0 to create unit tests for your SignalR application. SignalR 2.0 includes the IHubCallerConnectionContext interface, which can be used to create a mock object to simulate your hub methods for testing. In this section, you'll add unit tests for the application created in the Getting Started tutorial using XUnit.net andMoq . XUnit.net will be used to control the test; Moq will be used to create a mock object for testing. Other mocking frameworks can be used if desired; NSubstitute is also a good choice. This tutorial demonstrates how to set up the mock object in two ways: First, using a dynamic object (introduced in .NET Framework 4), and second, using an interface. Contents This tutorial contains the following sections. Unit testing with Dynamic Unit testing by type Unit testing with Dynamic In this section, you'll add a unit test for the application created in the Getting Started tutorial using a dynamic object. 1. Install the XUnit Runner extension for Visual Studio 2013. 2. Either complete the Getting Started tutorial , or download the completed application from MSDN Code Gallery . 3. If you are using the download version of the Getting Started application, open Package Manager Consoleand click Restore to add the SignalR package to the project. using System; using Xunit; using SignalRChat; using Microsoft.AspNet.SignalR.Hubs; using Moq; using System.Dynamic; namespace TestLibrary { public class Tests { [Fact]

description

Unitesting

Transcript of Unitesting

Unit testing SignalR applicationsYou can use the unit test features in SignalR 2.0 to create unit tests for your SignalR application. SignalR 2.0 includes theIHubCallerConnectionContextinterface, which can be used to create a mock object to simulate your hub methods for testing.In this section, you'll add unit tests for the application created in theGetting Started tutorialusingXUnit.netandMoq.XUnit.net will be used to control the test; Moq will be used to create amockobject for testing. Other mocking frameworks can be used if desired;NSubstituteis also a good choice. This tutorial demonstrates how to set up the mock object in two ways: First, using adynamicobject (introduced in .NET Framework 4), and second, using an interface.ContentsThis tutorial contains the following sections. Unit testing with Dynamic Unit testing by typeUnit testing with DynamicIn this section, you'll add a unit test for the application created in theGetting Started tutorialusing a dynamic object.1. Install theXUnit Runner extensionfor Visual Studio 2013.2. Either complete theGetting Started tutorial, or download the completed application fromMSDN Code Gallery.3. If you are using the download version of the Getting Started application, openPackage Manager Consoleand clickRestoreto add the SignalR package to the project.

using System;using Xunit;using SignalRChat;using Microsoft.AspNet.SignalR.Hubs;using Moq;using System.Dynamic;

namespace TestLibrary{ public class Tests { [Fact] public void HubsAreMockableViaDynamic() { bool sendCalled = false; var hub = new ChatHub(); var mockClients = new Mock(); hub.Clients = mockClients.Object; dynamic all = new ExpandoObject(); all.broadcastMessage = new Action((name, message) => { sendCalled = true; }); mockClients.Setup(m => m.All).Returns((ExpandoObject)all); hub.Send("TestUser", "TestMessage"); Assert.True(sendCalled); } }}

1. In the code above, a test client is created using theMockobject from theMoqlibrary, of typeIHubCallerConnectionContext. TheIHubCallerConnectionContextinterface is the proxy object with which you invoke methods on the client. ThebroadcastMessagefunction is then defined for the mock client so that it can be called by theChatHubclass. The test engine then calls theSendmethod of theChatHubclass, which in turn calls the mockedbroadcastMessagefunction.2. Build the solution by pressingF6.3. Run the unit test. In Visual Studio, selectTest,Windows,Test Explorer. In the Test Explorer window, right-clickHubsAreMockableViaDynamicand selectRun Selected Tests.

Unit testing by typeIn this section, you'll add a test for the application created in theGetting Started tutorialusing an interface that contains the method to be tested.1. Complete steps 1-7 in theUnit testing with Dynamictutorial above.2. Replace the contents of Tests.cs with the following code.3. using Xunit;4. using SignalRChat;5. using Microsoft.AspNet.SignalR.Hubs;6. using Moq;7. 8. 9. namespace TestLibrary10. {11. public class Tests12. {13. 14. public interface IClientContract15. {16. void broadcastMessage(string name, string message);17. }18. [Fact]19. public void HubsAreMockableViaType()20. {21. var hub = new ChatHub();22. var mockClients = new Mock();23. var all = new Mock();24. hub.Clients = mockClients.Object;25. all.Setup(m => m.broadcastMessage(It.IsAny(), 26. It.IsAny())).Verifiable();27. mockClients.Setup(m => m.All).Returns(all.Object);28. hub.Send("TestUser", "TestMessage");29. all.VerifyAll();30. }31. }32. }In the code above, an interface is created defining the signature of thebroadcastMessagemethod for which the test engine will create a mock client. A mock client is then created using theMockobject, of typeIHubCallerConnectionContext. TheIHubCallerConnectionContextinterface is the proxy object with which you invoke methods on the client.The test then creates an instance ofChatHub, and then creates a mock version of thebroadcastMessagemethod, which in turn is invoked by calling theSendmethod on the hub.33. Build the solution by pressingF6.34. Run the unit test. In Visual Studio, selectTest,Windows,Test Explorer. In the Test Explorer window, right-clickHubsAreMockableViaDynamicand selectRun Selected Tests.

Dependency injection is a way to remove hard-coded dependencies between objects, making it easier to replace an object's dependencies, either for testing (using mock objects) or to change run-time behavior. This tutorial shows how to perform dependency injection on SignalR hubs. It also shows how to use IoC containers with SignalR. An IoC container is a general framework for dependency injection.What is Dependency Injection?Skip this section if you are already familiar with dependency injection.Dependency injection(DI) is a pattern where objects are not responsible for creating their own dependencies. Here is a simple example to motivate DI. Suppose you have an object that needs to log messages. You might define a logging interface:interface ILogger { void LogMessage(string message);}In your object, you can create anILoggerto log messages:// Without dependency injection.class SomeComponent{ ILogger _logger = new FileLogger(@"C:\logs\log.txt");

public void DoSomething() { _logger.LogMessage("DoSomething"); }}This works, but its not the best design. If you want to replaceFileLoggerwith anotherILoggerimplementation, you will have to modifySomeComponent. Supposing that a lot of other objects useFileLogger, you will need to change all of them. Or if you decide to makeFileLoggera singleton, youll also need to make changes throughout the application.A better approach is to inject anILoggerinto the objectfor example, by using a constructor argument:// With dependency injection.class SomeComponent{ ILogger _logger;

// Inject ILogger into the object. public SomeComponent(ILogger logger) { if (logger == null) { throw new NullReferenceException("logger"); } _logger = logger; }

public void DoSomething() { _logger.LogMessage("DoSomething"); }}Now the object is not responsible for selecting whichILoggerto use. You can swichILoggerimplementations without changing the objects that depend on it.var logger = new TraceLogger(@"C:\logs\log.etl");var someComponent = new SomeComponent(logger);This pattern is calledconstructor injection. Another pattern is setter injection, where you set the dependency through a setter method or property.Simple Dependency Injection in SignalRConsider the Chat application from the tutorialGetting Started with SignalR 2.0. Here is the hub class from that application:public class ChatHub : Hub{ public void Send(string name, string message) { Clients.All.addMessage(name, message); }}Suppose that you want to store chat messages on the server before sending them. You might define an interface that abstracts this functionality, and use DI to inject the interface into theChatHubclass.public interface IChatRepository{ void Add(string name, string message); // Other methods not shown.}

public class ChatHub : Hub{ private IChatRepository _repository;

public ChatHub(IChatRepository repository) { _repository = repository; }

public void Send(string name, string message) { _repository.Add(name, message); Clients.All.addMessage(name, message); }The only problem is that a SignalR application does not directly create hubs; SignalR creates them for you. By default, SignalR expects a hub class to have a parameterless constructor. However, you can easily register a function to create hub instances, and use this function to perform DI. Register the function by callingGlobalHost.DependencyResolver.Register.public void Configuration(IAppBuilder app){ GlobalHost.DependencyResolver.Register( typeof(ChatHub), () => new ChatHub(new ChatMessageRepository()));

App.MapSignalR();

// ... }Now SignalR will invoke this anonymous function whenever it needs to create aChatHubinstance.IoC ContainersThe previous code is fine for simple cases. But you still had to write this:... new ChatHub(new ChatMessageRepository()) ...In a complex application with many dependencies, you might need to write a lot of this wiring code. This code can be hard to maintain, especially if dependencies are nested. It is also hard to unit test.One solution is to use an IoC container. An IoC container is a software component that is responsible for managing dependencies.You register types with the container, and then use the container to create objects. The container automatically figures out the dependency relations. Many IoC containers also allow you to control things like object lifetime and scope.Note: IoC stands for inversion of control, which is a general pattern where a framework calls into application code. An IoC container constructs your objects for you, which inverts the usual flow of control.Using IoC Containers in SignalRThe Chat application is probably too simple to benefit from an IoC container. Instead, lets look at theStockTickersample.The StockTicker sample defines two main classes: StockTickerHub: The hub class, which manages client connections. StockTicker: A singleton that holds stock prices and periodically updates them.StockTickerHubholds a reference to theStockTickersingleton, whileStockTickerholds a reference to theIHubConnectionContextfor theStockTickerHub. It uses this interface to communicate withStockTickerHubinstances. (For more information, seeServer Broadcast with ASP.NET SignalR.)We can use an IoC container to untangle these dependencies a bit. First, let's simplify theStockTickerHubandStockTickerclasses. In the following code, I've commented out the parts that we don't need.Remove the parameterless constructor fromStockTicker. Instead, we will always use DI to create the hub.[HubName("stockTicker")]public class StockTickerHub : Hub{ private readonly StockTicker _stockTicker;

//public StockTickerHub() : this(StockTicker.Instance) { }

public StockTickerHub(StockTicker stockTicker) { if (stockTicker == null) { throw new ArgumentNullException("stockTicker"); } _stockTicker = stockTicker; }

// ...For StockTicker, remove the singleton instance. Later, we'll use the IoC container to control the StockTicker lifetime. Also, make the constructor public.public class StockTicker{ //private readonly static Lazy _instance = new Lazy( // () => new StockTicker(GlobalHost.ConnectionManager.GetHubContext().Clients));

// Important! Make this constructor public. public StockTicker(IHubConnectionContext clients) { if (clients == null) { throw new ArgumentNullException("clients"); }

Clients = clients; LoadDefaultStocks(); }

//public static StockTicker Instance //{ // get // { // return _instance.Value; // } //}Next, we can refactor the code by creating an interface forStockTicker. Well use this interface to decouple theStockTickerHubfrom theStockTickerclass.Visual Studio makes this kind of refactoring easy. Open the file StockTicker.cs, right-click on theStockTickerclass declaration, and selectRefactor...Extract Interface.

In theExtract Interfacedialog, clickSelect All. Leave the other defaults. ClickOK.

Visual Studio creates a new interface namedIStockTicker, and also changesStockTickerto derive fromIStockTicker.Open the file IStockTicker.cs and change the interface topublic.public interface IStockTicker{ void CloseMarket(); IEnumerable GetAllStocks(); MarketState MarketState { get; } void OpenMarket(); void Reset();}In theStockTickerHubclass, change the two instances ofStockTickertoIStockTicker:[HubName("stockTicker")]public class StockTickerHub : Hub{ private readonly IStockTicker _stockTicker;

public StockTickerHub(IStockTicker stockTicker) { if (stockTicker == null) { throw new ArgumentNullException("stockTicker"); } _stockTicker = stockTicker; }Creating anIStockTickerinterface isnt strictly necessary, but I wanted to show how DI can help to reduce coupling between components in your application.Add the Ninject LibraryThere are many open-source IoC containers for .NET. For this tutorial, Ill useNinject. (Other popular libraries includeCastle Windsor,Spring.Net,Autofac,Unity, andStructureMap.)Use NuGet Package Manager to install theNinject library. In Visual Studio, from theToolsmenu selectLibrary Package Manager|Package Manager Console. In the Package Manager Console window, enter the following command: Install-Package Ninject -Version 3.0.1.10 Replace the SignalR Dependency ResolverTo use Ninject within SignalR, create a class that derives fromDefaultDependencyResolver.internal class NinjectSignalRDependencyResolver : DefaultDependencyResolver{ private readonly IKernel _kernel; public NinjectSignalRDependencyResolver(IKernel kernel) { _kernel = kernel; }

public override object GetService(Type serviceType) { return _kernel.TryGet(serviceType) ?? base.GetService(serviceType); }

public override IEnumerable GetServices(Type serviceType) { return _kernel.GetAll(serviceType).Concat(base.GetServices(serviceType)); }}This class overrides theGetServiceandGetServicesmethods ofDefaultDependencyResolver. SignalR calls these methods to create various objects at runtime, including hub instances, as well as various services used internally by SignalR. TheGetServicemethod creates a single instance of a type. Override this method to call the Ninject kernel'sTryGetmethod. If that method returns null, fall back to the default resolver. TheGetServicesmethod creates a collection of objects of a specified type. Override this method to concatenate the results from Ninject with the results from the default resolver.Configure Ninject BindingsNow well use Ninject to declare type bindings.Open the file RegisterHubs.cs. In theRegisterHubs.Startmethod, create the Ninject container, which Ninject calls thekernel.var kernel = new StandardKernel();Create an instance of our custom dependency resolver:var resolver = new NinjectSignalRDependencyResolver(kernel);Create a binding forIStockTickeras follows:kernel.Bind() .To() // Bind to StockTicker. .InSingletonScope(); // Make it a singleton object.This code is saying two things. First, whenever the application needs anIStockTicker, the kernel should create an instance ofStockTicker. Second, theStockTickerclass should be a created as a singleton object. Ninject will create one instance of the object, and return the same instance for each request.Create a binding forIHubConnectionContextas follows:kernel.Bind().ToMethod(context => resolver.Resolve().GetHubContext().Clients ).WhenInjectedInto();This code creatres an anonymous function that returns anIHubConnection. TheWhenInjectedIntomethod tells Ninject to use this function only when creatingIStockTickerinstances. The reason is that SignalR createsIHubConnectionContextinstances internally, and we don't want to override how SignalR creates them. This function only applies to ourStockTickerclass.Pass the dependency resolver into theMapSignalRmethod:App.MapSignalR(config);Now SignalR will use the resolver specified inMapSignalR, instead of the default resolver.Here is the complete code listing forRegisterHubs.Start.public static class RegisterHubs{ public static void Start() { var kernel = new StandardKernel(); var resolver = new NinjectSignalRDependencyResolver(kernel);

kernel.Bind() .To() .InSingletonScope();

kernel.Bind().ToMethod(context => resolver.Resolve(). GetHubContext().Clients ).WhenInjectedInto();

var config = new HubConfiguration() { Resolver = resolver };

App.MapSignalR(config); }}To run the StockTicker application in Visual Studio, press F5. In the browser window, navigate to http://localhost:port/SignalR.Sample/StockTicker.html.