C# 如何为使用OWIN、信号器和Selenium/量角器的web应用程序执行E2E测试?我只能通过第一个测试
我正在尝试执行E2E测试,在每个测试中,我创建一个新网站并回滚一个数据库。我正在使用信号器将数据推送到客户端。我的问题是,当我一次运行所有测试时,我的第一个测试才通过,即使每个测试都单独通过我如何连续运行一系列测试,在每次运行之间清理SignalR/Owin?我找不到任何SignalR+E2E测试的示例,尽管官方文档确实显示了单元测试 虽然进行了一些推断和记录,但我认为是信号器未正确处理和/或初始化。以下是我所做的,以确认这是问题所在:C# 如何为使用OWIN、信号器和Selenium/量角器的web应用程序执行E2E测试?我只能通过第一个测试,c#,selenium,signalr,owin,xunit.net,C#,Selenium,Signalr,Owin,Xunit.net,我正在尝试执行E2E测试,在每个测试中,我创建一个新网站并回滚一个数据库。我正在使用信号器将数据推送到客户端。我的问题是,当我一次运行所有测试时,我的第一个测试才通过,即使每个测试都单独通过我如何连续运行一系列测试,在每次运行之间清理SignalR/Owin?我找不到任何SignalR+E2E测试的示例,尽管官方文档确实显示了单元测试 虽然进行了一些推断和记录,但我认为是信号器未正确处理和/或初始化。以下是我所做的,以确认这是问题所在: 于2013年开放Visual Studio 创建了一个使用
Microsoft.AspNet.signal
angularjs
(我的网站使用angular;为了尽可能接近我的真实环境,我使用的是量角器,而不是Selenium)StartUp
的类:ChatHub
的类:索引的html文件:
xunit
量角器
Selenium.WebDriver.ChromeDriver
Microsoft.Owin.Hosting
Microsoft.Owin.Host.HttpListener
Microsoft.Owin.StaticFiles
MyTest
的类:using Microsoft.Owin;
using Owin;
namespace SignalRSandbox
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
}
using System;
using System.Web;
using Microsoft.AspNet.SignalR;
namespace SignalRSandbox
{
public class ChatHub : Hub
{
public void Send(string name, string message)
{
Clients.All.broadcastMessage(name, message);
}
}
}
using Microsoft.Owin.Hosting;
using OpenQA.Selenium.Chrome;
using Protractor;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xunit;
using Owin;
using Microsoft.Owin.StaticFiles;
using Microsoft.Owin.FileSystems;
using OpenQA.Selenium;
namespace SignalRSandbox.Tests
{
public class MyTest : IDisposable
{
private NgWebDriver _driver;
private IDisposable _site;
private string _url;
public MyTest()
{
this._url = "http://localhost:8765/index.html";
this._site = WebApp.Start(this._url, appBuilder =>
{
// SignalR
new Startup().Configuration(appBuilder);
// Allow static content through
var options = new FileServerOptions();
options.FileSystem = new PhysicalFileSystem("../../../SignalRSandbox");
appBuilder.UseFileServer(options);
});
this._driver = new NgWebDriver(new ChromeDriver());
this._driver.Manage().Timeouts().SetPageLoadTimeout(TimeSpan.FromMinutes(1));
this._driver.Manage().Timeouts().SetScriptTimeout(TimeSpan.FromMinutes(1));
this._driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(2));
// We have to use the WrappedDriver otherwise the alert will cause an error.
this._driver.WrappedDriver.Navigate().GoToUrl(this._url);
// Enter the name
var alert = this._driver.WrappedDriver.SwitchTo().Alert();
alert.SendKeys("j");
alert.Accept();
// Enter a message
this._driver.WrappedDriver.FindElement(By.Id("message")).SendKeys("test");
this._driver.WrappedDriver.FindElement(By.Id("send")).Click();
// Enter another message
this._driver.WrappedDriver.FindElement(By.Id("message")).SendKeys("test1");
this._driver.WrappedDriver.FindElement(By.Id("send")).Click();
Assert.Equal("j: test", this._driver.WrappedDriver.FindElement(By.Id("discussion0")).Text);
Assert.Equal("j: test1", this._driver.WrappedDriver.FindElement(By.Id("discussion1")).Text);
}
[Fact]
public void Test()
{
}
// All subsequent tests fail
[Fact]
public void Test1()
{
}
[Fact]
public void Test2()
{
}
[Fact]
public void Test3()
{
}
public void Dispose()
{
if (this._driver != null)
{
this._driver.Dispose();
}
if (this._site != null)
{
this._site.Dispose();
}
}
}
}
ChatHub.cs
using Microsoft.Owin;
using Owin;
namespace SignalRSandbox
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
}
using System;
using System.Web;
using Microsoft.AspNet.SignalR;
namespace SignalRSandbox
{
public class ChatHub : Hub
{
public void Send(string name, string message)
{
Clients.All.broadcastMessage(name, message);
}
}
}
using Microsoft.Owin.Hosting;
using OpenQA.Selenium.Chrome;
using Protractor;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xunit;
using Owin;
using Microsoft.Owin.StaticFiles;
using Microsoft.Owin.FileSystems;
using OpenQA.Selenium;
namespace SignalRSandbox.Tests
{
public class MyTest : IDisposable
{
private NgWebDriver _driver;
private IDisposable _site;
private string _url;
public MyTest()
{
this._url = "http://localhost:8765/index.html";
this._site = WebApp.Start(this._url, appBuilder =>
{
// SignalR
new Startup().Configuration(appBuilder);
// Allow static content through
var options = new FileServerOptions();
options.FileSystem = new PhysicalFileSystem("../../../SignalRSandbox");
appBuilder.UseFileServer(options);
});
this._driver = new NgWebDriver(new ChromeDriver());
this._driver.Manage().Timeouts().SetPageLoadTimeout(TimeSpan.FromMinutes(1));
this._driver.Manage().Timeouts().SetScriptTimeout(TimeSpan.FromMinutes(1));
this._driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(2));
// We have to use the WrappedDriver otherwise the alert will cause an error.
this._driver.WrappedDriver.Navigate().GoToUrl(this._url);
// Enter the name
var alert = this._driver.WrappedDriver.SwitchTo().Alert();
alert.SendKeys("j");
alert.Accept();
// Enter a message
this._driver.WrappedDriver.FindElement(By.Id("message")).SendKeys("test");
this._driver.WrappedDriver.FindElement(By.Id("send")).Click();
// Enter another message
this._driver.WrappedDriver.FindElement(By.Id("message")).SendKeys("test1");
this._driver.WrappedDriver.FindElement(By.Id("send")).Click();
Assert.Equal("j: test", this._driver.WrappedDriver.FindElement(By.Id("discussion0")).Text);
Assert.Equal("j: test1", this._driver.WrappedDriver.FindElement(By.Id("discussion1")).Text);
}
[Fact]
public void Test()
{
}
// All subsequent tests fail
[Fact]
public void Test1()
{
}
[Fact]
public void Test2()
{
}
[Fact]
public void Test3()
{
}
public void Dispose()
{
if (this._driver != null)
{
this._driver.Dispose();
}
if (this._site != null)
{
this._site.Dispose();
}
}
}
}
Index.html(有些代码可能看起来有点古怪-我只是想让Gradurator/Selenium正常等待)
测试是使用测试资源管理器运行的(我想我也安装了一个XUnit.netvisualstudio插件)
更新:通过切换到NUnit而不是xUnit并使用扩展名,我可以执行以下操作,但在这样做的过程中,我最终在测试方法中调用了Initialize和dispose,我不想重复,但它确实完成了任务 如果有人不使用单独的应用程序域就可以工作,我会接受 以下是更新后的MyTest.cs第2版:
using Microsoft.Owin.Hosting;
using OpenQA.Selenium.Chrome;
using Protractor;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Owin;
using Microsoft.Owin.StaticFiles;
using Microsoft.Owin.FileSystems;
using OpenQA.Selenium;
using NUnit.Framework;
namespace SignalRSandbox.Tests
{
public class MyTest
{
private NgWebDriver _driver;
private IDisposable _site;
private string _url;
public void Intialize()
{
this._url = "http://localhost:8765";
this._site = WebApp.Start(this._url, appBuilder =>
{
// SignalR
new Startup().Configuration(appBuilder);
// Allow static content through
var options = new FileServerOptions();
options.FileSystem = new PhysicalFileSystem("../../../SignalRSandbox");
appBuilder.UseFileServer(options);
});
this._driver = new NgWebDriver(new ChromeDriver());
this._driver.Manage().Timeouts().SetPageLoadTimeout(TimeSpan.FromMinutes(1));
this._driver.Manage().Timeouts().SetScriptTimeout(TimeSpan.FromMinutes(1));
this._driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(2));
// We have to use the WrappedDriver otherwise the alert will cause an error.
this._driver.WrappedDriver.Navigate().GoToUrl(this._url);
// Enter the name
var alert = this._driver.WrappedDriver.SwitchTo().Alert();
alert.SendKeys("j");
alert.Accept();
// Enter a message
this._driver.WrappedDriver.FindElement(By.Id("message")).SendKeys("test");
this._driver.WrappedDriver.FindElement(By.Id("send")).Click();
// Enter another message
this._driver.WrappedDriver.FindElement(By.Id("message")).SendKeys("test1");
this._driver.WrappedDriver.FindElement(By.Id("send")).Click();
Assert.AreEqual("j: test", this._driver.WrappedDriver.FindElement(By.Id("discussion0")).Text);
Assert.AreEqual("j: test1", this._driver.WrappedDriver.FindElement(By.Id("discussion1")).Text);
}
[Test, RunInApplicationDomain]
public void Test()
{
this.Intialize();
this.Dispose();
}
// All subsequent tests fail
[Test, RunInApplicationDomain]
public void Test1()
{
this.Intialize();
this.Dispose();
}
[Test, RunInApplicationDomain]
public void Test2()
{
this.Intialize();
this.Dispose();
}
[Test, RunInApplicationDomain]
public void Test3()
{
this.Intialize();
this.Dispose();
}
public void Dispose()
{
if (this._driver != null)
{
this._driver.Dispose();
}
if (this._site != null)
{
this._site.Dispose();
}
}
}
}
如果要在同一AppDomain中多次调用
MapSignalR
,每次都应传入一个新的DefaultDependencyResolver
。如果您没有将自己的idependencysolver
传递到mapsigner
,它将使用由GlobalHost.dependencysolver
引用的
您看到的测试失败可能是由于您在处理MyTest时处理了信号器依赖项而导致的
您可以为每次调用Configure
指定自己的idependencysolver
,如下所示:
using Microsoft.AspNet.SignalR;
using Microsoft.Owin;
using Owin;
namespace SignalRSandbox
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR(new HubConfiguration
{
Resolver = new DefaultDependencyResolver()
});
}
}
}
EDIT:上述解决方案将使GlobalHost
不可用,因为每个GlobalHost
属性都引用了GlobalHost.DependencyResolver
,信号器不再使用该属性
您可以通过解析实际使用的idependencysolver
信号器中的适当类型来解决此问题。例如,您可以执行以下操作,而不是使用GlobalHost.ConnectionManager
:
public void Configuration(IAppBuilder app)
{
var resolver = new DefaultDependencyResolver();
var connectionManager = resolver.Resolve<IConnectionManager>();
var myHubContext = connectionManager.GetHubContext<MyHub>();
app.MapSignalR(new HubConfiguration
{
Resolver = resolver
});
}
谢谢回复!添加:
config.Resolver=new DefaultDependencyResolver()
会使信号器看起来似乎会导致我的两个集线器之一开始工作。如果我像这样设置依赖项解析器,我可以这样做:“GlobalHost.ConnectionManager.GetHubContext().Clients.All.myMethod()”?这是唯一奇怪的地方。我编辑了我的答案,解释了为什么如果您提供自定义IDependencyResolver
,GlobalHost会停止工作。谢谢!“或者,由于您没有同时运行多个SignalR端点…”这是否意味着这只适用于该集线器上的单个集线器/单个端点(我有一个客户端方法和一个服务器方法,实际上每个都在不同的集线器上)?所有集线器共享一个SignalR端点(默认路径为“/SignalR”)。PersistentConnections总是有自己的端点,但您似乎没有使用它们。我想说的主要一点是,如果您在配置方法中多次调用MapSignalR
(如果您使用重载来调用MapSignalR
,则可以这样做),并且您不希望两个端点相互干扰,您不应该使用GlobalHost
,因为这会导致端点共享状态。明白了!所以我在WebApi中进行信号器配置。在WebApi之后启动。事实证明,Web API配置实例化了依赖于signerIHubContext
的内容。点是它的工作后,我扭转了两个,所以信号器是配置第一。我已经在我的网站上尝试了2-3天的E2E测试(不同的问题)。一分钟前,这三个系统第一次按顺序运行。非常感谢:)