Hub を SignalR でテストしたいのですが、最適な方法は何ですか?
これまでに考えた可能な解決策:
- テスト可能なハブを作成する
- クラスを分離する抽象ロジック
- Selenium (小さなユニットをテストしたい)
- それとも、一部の SignalR テスト機能が見過ごされているのでしょうか
現在、SignalR 0.4 と NUnit をテスト フレームワークとして使用しています。
Hub を SignalR でテストしたいのですが、最適な方法は何ですか?
これまでに考えた可能な解決策:
現在、SignalR 0.4 と NUnit をテスト フレームワークとして使用しています。
このリンクは、Moq を使用して SignalR ハブ メソッドを単体テストする方法を示しています。リポジトリ、クライアント、コンテキスト、および呼び出し元をモックアップします。サイトのコードは次のとおりです。最新の SignalR で動作するようにいくつかの小さな変更を加えました。
public class TestableChatHub : ChatHub
{
public Mock<IChatRepository> MockChatRepository { get; private set; }
public TestableChatHub(Mock<IChatRepository> mockChatRepository)
: base(mockChatRepository.Object)
{
const string connectionId = "1234";
const string hubName = "Chat";
var mockConnection = new Mock<IConnection>();
var mockUser = new Mock<IPrincipal>();
var mockCookies = new Mock<IRequestCookieCollection>();
var mockRequest = new Mock<IRequest>();
mockRequest.Setup(r => r.User).Returns(mockUser.Object);
mockRequest.Setup(r => r.Cookies).Returns(mockCookies.Object);
Clients = new ClientProxy(mockConnection.Object, hubName);
Context = new HubCallerContext(mockRequest.Object, connectionId);
var trackingDictionary = new TrackingDictionary();
Caller = new StatefulSignalProxy(
mockConnection.Object, connectionId, hubName, trackingDictionary);
}
}
次に、サイトは、このテスト可能なハブを使用して単体テストを作成できることを示しています。
[TestClass]
public class ChatHubTests
{
private TestableChatHub _hub;
public void SetUpTests()
{
_hub = GetTestableChatHub();
}
[Test]
public void ExampleTest()
{
SetUpTests();
const string message = "test";
const string connectionId = "1234";
var result = _hub.Send(message);
_hub.MockChatRepository.Verify(r => r.SaveMessage(message, connectionId));
Assert.IsTrue(result);
}
private TestableChatHub GetTestableChatHub()
{
var mockRepository = new Mock<IChatRepository>();
mockRepository.Setup(m => m.SaveMessage(
It.IsAny<string>(), It.IsAny<string())).Returns(true);
return new TestableChatHub(mockRepository);
}
}
いくつかの優れたトリックを使用して、単体テスト用の SignalR ハブを作成するのは非常に簡単です。注意すべきことの 1 つは、SignalR はdynamic
、モック フレームワークでサポートされていない可能性のあるクラスを使用することです (私はNSubstituteを使用します)。
public class ProjectsHub: Hub
{
public void AddProject(string id)
{
Clients.All.AddProject(id);
}
}
[TestFixture]
public class ProjectsHubTests
{
// Operations that clients might receive
// This interface is in place in order to mock the
// dynamic object used in SignalR
public interface ISignals
{
void AddProject(string id);
}
[Test]
public void AddProject_Broadcasts()
{
// Arrange
ProjectsHub hub = new ProjectsHub();
IHubCallerConnectionContext clients =
Substitute.For<IHubCallerConnectionContext>();
ISignals signals = Substitute.For<ISignals>();
SubstituteExtensions.Returns(clients.All, signals);
hub.Clients = clients;
// Act
hub.AddProject("id");
// Assert
signals.Received(1).AddProject("id");
}
}
これを書き換えて、たとえば Moq を使用するのは非常に簡単です。
この質問は少し前のものですが、とにかく答えるために最善を尽くします。
実際のハブ クラスに多くのロジックがある場合、ロジックを別のクラスに抽象化することは確かに理にかなっています。SignalR を利用したマルチプレイヤー デモでも同じことを行いました。ハブ クラス自体で実行する必要がある唯一の動作は、メッセージングに関連するものです。それ以降のすべてのアクションは委任する必要があります。
注: これは、ASP .NET MVC のコントローラー設計のガイドラインに非常によく似ています。コントローラーを小さく保ち、実際の作業を委任します。
実際に何らかの作業を行う SignalR との統合テストが必要な場合は、selenium webdriver が適しています。ただし、SignalR メッセージングがテストのコンテキストで完全に機能するようにするには、おそらく何らかの調整が必要になるでしょう。「signalr selenium」(引用符なし) を Google 検索して、正しい軌道に乗ってください。
SignalR 2.0 を使用すると、次のように実行できます。
// Arrange
var hub = new CodeInteractivePreviewHub();
var mockClients = new Mock<IHubCallerConnectionContext<dynamic>>();
hub.Clients = mockClients.Object;
dynamic all = new ExpandoObject();
mockClients.Setup(m => m.All).Returns((ExpandoObject)all);
// Act
var allSourceCodes = hub.InitiateCommunication(); //Change this line to your Hub's method
// Assert
Assert.IsNotNull(allSourceCodes);
私のインターフェースはテスト プロジェクトから外れていましたが、NUnit と Moq を使用して作成した方法を次に示します。
using Microsoft.AspNetCore.SignalR;
using Moq;
using NUnit.Framework;
namespace TestLibrary
{
// Operations that clients might receive
// This interface is in place in order to mock the
// dynamic object used in SignalR
public interface ISignals
{
void AddProject(string id);
}
public class ProjectsHub : Hub<ISignals>
{
public void AddProject(string id)
{
Clients.All.AddProject(id);
}
}
public class ProjectsHubTests
{
[Test]
public void AddProject_Broadcasts()
{
// Arrange
ProjectsHub hub = new ProjectsHub();
var clients = new Mock<IHubCallerClients<ISignals>>();
var signals = new Mock<ISignals>();
hub.Clients = clients.Object;
signals.Setup(m => m.AddProject(It.Is<string>(s => s == "id"))).Verifiable();
clients.Setup(m => m.All).Returns(signals.Object);
// Act
hub.AddProject("id");
// Assert
signals.VerifyAll();
}
}
}