Можно ли заставить C # Facebook SDK использовать HTTP вместо HTTPS? - PullRequest
1 голос
/ 19 марта 2012

Мне нужно выполнить некоторые симуляции подключения, чтобы убедиться, что мой код обрабатывает различные ошибки подключения к Facebook. Я хочу иметь возможность симулировать 500 секунд, тайм-ауты и т. Д.

Самый простой способ сделать это - использовать Fiddler, но, похоже, он не работает с HTTPS (при попытке получить 403 с).

Есть ли способ заставить SDK работать с HTTP вместо HTTPS для целей отладки?

Ответы [ 2 ]

4 голосов
/ 19 марта 2012

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.

Для версии 5 вам нужно переопределить защищенный метод CreateHttpWebRequest в FacebookClient.

Вот пример для 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.

Создайте свой собственный HttpWebRequest и HttpWebResponse, унаследовав HttpWebRequestWrapper и HttpWebReponseWrapper.

Затем измените фабрику http-запросов по умолчанию для Facebook C # SDK. Вот образец фабрики по умолчанию.

FacebookClient.SetDefaultHttpWebRequestFactory(uri => new HttpWebRequestWrapper((HttpWebRequest)WebRequest.Create(uri)));

Если вы хотите изменить HttpWebRequestFactor для экземпляра FacebookClient, используйте следующий код.

var fb = new FacebookClient();
fb.HttpWebRequestFactory = uri=> new MyHttpWebRequestWrapper(uri); 

Примечание. HttpWebRequestWrapper, HttpWebResponseWrapper, WebExceptionWrapper, FacebookClient.SetDefaultHttpWebRequestFactory и FacebookClient.HttpWebRequestFactory имеют атрибут [EditorBrowsable(EditorBrowsableState.Never)], поэтому вы можете не видеть его в intellisense.

Такие вещи, как отсутствие интернет-соединения, о котором вы упомянули, должны быть частью тестов facebook c # sdk, а не модульных тестов вашего приложения. SDK должен гарантировать, что когда нет соединения с Интернетом, он всегда выдает WebExceptionWrapper, и ваши модульные тесты должны фактически обрабатывать исключение WebExceptionWrapper, а не насмехаться над всем httpwebrequest и httpwebresponse.

0 голосов
/ 19 марта 2012

Я бы предложил вам ввести другой уровень абстракции для вашего кода, а код для этой абстракции, а не для реализации. Например.

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, все это может быть автоматически подключено к вам. Фреймворки MVVM, такие как Caliburn.Micro, как правило, также поддерживают это.

Затем, когда дело доходит до модульного тестирования (или ручного тестирования), вы можете изменить реализацию вашего интерфейса;

public class Http403Client : IFacebookClient {
  public IEnumerable<Friend> GetFriends() {
    throw new HttpException(403, "Forbidden");
  }
}

Очевидно, что это всего лишь пример макета, но я думаю, что он демонстрирует концепцию, которую вы хотите реализовать.

...