我需要进行一些连接模拟,以查看我的代码处理Facebook的各种连接错误。我希望能够模拟500秒,超时等。
最简单的方法是使用Fiddler,但似乎无法使用HTTPS(我尝试时会获得403s)。
是否可以强制SDK使用HTTP而不是HTTPS来进行调试?
答案 0 :(得分:4)
Facebook C#SDK支持您模拟整个HttpWebRequest和HttpWebResponse的场景。事实上,我们实际上在单元测试中使用它,以便Facebook C#SDK中的每一行代码实际执行并且结果始终相同。 https://github.com/facebook-csharp-sdk/facebook-csharp-sdk/blob/v5/Source/Facebook.Tests/TestExtensions.cs目前您需要在v5分支中检查这些测试,因为我们还没有将这些测试迁移到v6。
对于v5,您需要覆盖FacebookClient中受保护的CreateHttpWebRequest
方法。
以下是没有互联网连接时v5的示例。你需要使用三个隐藏类HttpWebRequestWrapper,HttpWebResponseWrapper和WebExceptionWrapper。
public static void NoInternetConnection(this Mock<Facebook.FacebookClient> facebookClient, out Mock<HttpWebRequestWrapper> mockRequest, out Mock<WebExceptionWrapper> mockWebException)
{
mockRequest = new Mock<HttpWebRequestWrapper>();
mockWebException = new Mock<WebExceptionWrapper>();
var mockAsyncResult = new Mock<IAsyncResult>();
var request = mockRequest.Object;
var webException = mockWebException.Object;
var asyncResult = mockAsyncResult.Object;
mockRequest.SetupProperty(r => r.Method);
mockRequest.SetupProperty(r => r.ContentType);
mockRequest.SetupProperty(r => r.ContentLength);
mockAsyncResult
.Setup(ar => ar.AsyncWaitHandle)
.Returns((ManualResetEvent)null);
mockWebException
.Setup(e => e.GetResponse())
.Returns<HttpWebResponseWrapper>(null);
mockRequest
.Setup(r => r.GetResponse())
.Throws(webException);
mockRequest
.Setup(r => r.EndGetResponse(It.IsAny<IAsyncResult>()))
.Throws(webException);
AsyncCallback callback = null;
mockRequest
.Setup(r => r.BeginGetResponse(It.IsAny<AsyncCallback>(), It.IsAny<object>()))
.Callback<AsyncCallback, object>((c, s) =>
{
callback = c;
})
.Returns(() =>
{
callback(asyncResult);
return asyncResult;
});
var mockRequestCopy = mockRequest;
var mockWebExceptionCopy = mockWebException;
facebookClient.Protected()
.Setup<HttpWebRequestWrapper>("CreateHttpWebRequest", ItExpr.IsAny<Uri>())
.Callback<Uri>(uri =>
{
mockRequestCopy.Setup(r => r.RequestUri).Returns(uri);
mockWebExceptionCopy.Setup(e => e.Message).Returns(string.Format("The remote name could not be resolved: '{0}'", uri.Host));
})
.Returns(request);
}
然后您可以按照以下方式编写测试。
[Fact]
public void SyncWhenThereIsNotInternetConnectionAndFiddlerIsNotOpen_ThrowsWebExceptionWrapper()
{
var mockFb = new Mock<FacebookClient> { CallBase = true };
Mock<HttpWebRequestWrapper> mockRequest;
Mock<WebExceptionWrapper> mockWebException;
mockFb.NoInternetConnection(out mockRequest, out mockWebException);
Exception exception = null;
try
{
var fb = mockFb.Object;
fb.Get(_parameters);
}
catch (Exception ex)
{
exception = ex;
}
mockFb.VerifyCreateHttpWebRequest(Times.Once());
mockRequest.VerifyGetResponse();
mockWebException.VerifyGetReponse();
Assert.IsAssignableFrom<WebExceptionWrapper>(exception);
}
在v6中,我们更容易模拟HttpWebRequest和HttpWebResponse。
通过继承HttpWebRequestWrapper
和HttpWebReponseWrapper
来创建自定义HttpWebRequest和HttpWebResponse。
然后更改Facebook C#SDK的默认http Web请求工厂。以下是默认工厂的示例。
FacebookClient.SetDefaultHttpWebRequestFactory(uri => new HttpWebRequestWrapper((HttpWebRequest)WebRequest.Create(uri)));
如果您想根据FacebookClient实例更改HttpWebRequestFactor,请使用以下代码。
var fb = new FacebookClient();
fb.HttpWebRequestFactory = uri=> new MyHttpWebRequestWrapper(uri);
注意:HttpWebRequestWrapper,HttpWebResponseWrapper,WebExceptionWrapper,FacebookClient.SetDefaultHttpWebRequestFactory和FacebookClient.HttpWebRequestFactory都有属性[EditorBrowsable(EditorBrowsableState.Never)]
,所以你可能在intellisense中看不到它。
你提到的没有互联网连接的东西实际上应该是facebook c#sdk测试的一部分,而不是你的app单元测试。 sdk应该保证,当没有互联网连接时,它总是抛出WebExceptionWrapper,你的app单元测试实际上应该处理WebExceptionWrapper异常而不是模拟整个httpwebrequest和httpwebresponse。
答案 1 :(得分:0)
我建议你为代码和代码引入另一层抽象而不是实现。例如
public interface IFacebookClient {
IEnumerable<Friend> GetFriends();
}
public class HttpsClient : IFacebookClient {
public IEnumerable<Friend> GetFriends() {
// Make a call out to the Facebook API, as per usual
};
}
在您的消费代码中,您可以执行以下操作:
public class ConsumingCode {
private IFacebookClient _client;
public ConsumingCode(IFacebookClient client) {
_client = client;
foreach (Friend friend in _client.GetFriends()) {
// Do something with each Friend
}
}
}
如果您正在使用IoC容器,则可以自动为您连接。像Caliburn.Micro这样的MVVM框架也倾向于支持这一点。
然后,当涉及到单元测试(或手动测试)时,您可以更改界面的实现;
public class Http403Client : IFacebookClient {
public IEnumerable<Friend> GetFriends() {
throw new HttpException(403, "Forbidden");
}
}
显然这只是一个模拟示例,但我认为它演示了您要实现的概念。