可能强制C#Facebook SDK使用HTTP而不是HTTPS?

可能强制C#Facebook SDK使用HTTP而不是HTTPS?

问题描述:

我需要做一些连接模拟来查看我的代码处理Facebook的各种连接错误。我希望能够模拟500s,超时等。可能强制C#Facebook SDK使用HTTP而不是HTTPS?

最简单的方法是使用Fiddler,但它似乎不能与HTTPS(我尝试时获得403s)一起工作。

其他方法可以强制SDK使用HTTP而不是HTTPS进行调试吗?

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); 
    } 

在第6版中,我们嘲笑HttpWebRequest和HttpWebResponse要容易得多。

通过继承HttpWebRequestWrapperHttpWebReponseWrapper来创建您的自定义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)],因此您可能无法在智能感知中看到它。

你提到的没有互联网连接的东西实际上应该是facebook c#sdk测试的一部分,而不是你的应用程序单元测试。 sdk应该保证当没有互联网连接时,它总是抛出WebExceptionWrapper,你的应用程序单元测试实际上应该处理WebExceptionWrapper异常,而不是模拟整个httpwebrequest和httpwebresponse。

+0

谢谢!这就是我一直在寻找的东西。 – 2012-03-20 03:29:51

我建议你为你的代码和代码引入另一个抽象层次,而不是实现。例如。

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"); 
    } 
} 

显然,这仅仅是一个实物模型的例子,但我认为它证明了你要实现的概念。

+0

感谢您的回答,但我想测试来自网络的“真实”不良反应。我的代码听起来很合理,我已经用类似于(但更简单)的方法测试了它,而不是你的建议。 Facebook C#库虽然有很多代码,但当它收到错误的HTTP响应时,我需要查看它与代码结合的行为。 – 2012-03-19 06:03:10

+0

在一天结束时,它仍然是你的调用代码响应C#库返回的东西的方式。如果是特定情况 - 当我调用GetFoo()时,在调用HasFoo()之后,发生了一些不好的情况,我需要调用Reset() - 然后将该场景构建到伪装的实现中。您可能希望将传递调用与真实实现结合起来,并根据场景返回固定结果/投入其他人。但是这种交互测试非常适合自动化单元测试。 – MrMDavidson 2012-03-19 06:12:49

+0

再次,谢谢 - 我很欣赏你的建议。但这不是我的问题的答案,我对自动化测试不感兴趣。我需要测试一个我几乎不能控制的组件(C#SDK),我可以做的唯一方法就是进入并向该组件添加代码(这意味着我需要维护我的测试代码放在“生活”组件中,或者使组件对HTTP工作(所以我可以在一次性手动测试中伪造失败)。对于有人会询问自动测试的问题,这是一个很好的建议,但我是对此不感兴趣。 – 2012-03-19 06:18:19