문제

모델 / 뷰 / 뷰 모델 패턴을 배우고 변형 (Datamodel / View / ViewModel 또는 모델 /보기 / 발표자)입니다.

내가 궁금한 점 : WCF 서비스와 함께이 패턴을 사용하는 경우 모델 (Datamodel) 서비스입니다. 아니면 WCF 서비스 계층을 캡슐화하려면 별도의 모델이 필요합니까 ??

WCF에 대한 호출이 연결을 관리해야하므로 WCF를 Datamodel로 사용하면 WCF 서비스를 전체 WCF 서비스를 조롱하지 않고 테스트 할 수 없습니다. 이 뷰 모델의 통화는 다음과 같습니다.

List<Sam.Alyza.WcfInterface.Website> rc = null;
Service<Sam.Alyza.WcfInterface.IServiceWebsites>.Use(alyzaSvc =>
{
  rc = new List<Sam.Alyza.WcfInterface.Website>(alyzaSvc.GetSites());
});

내 ViewModel을 테스트 할 수 있도록 WCF 연결을 추상화하기 위해 별도의 데이터 모드를 추가하려고 시도했습니다. 이 후에 뷰 모델이 테스트 가능했으며, 호출은 다음과 같이 보였습니다.

List<Sam.Alyza.WcfInterface.Website> rc = new List<Sam.Alyza.WcfInterface.Website>(_datamodel.GetSites());

문제 : 테스트해야 할 대부분의 코드는 이제 데이터 모드로 이동하여 WCF를 테스트해야했습니다. 뷰 모델에 남아있는 것은 테스트 할 수있는 얇은 쉘이었습니다. 그러나 기본 코드가 데이터 모드로 이동 한 이후 뷰 모델을 테스트하는 것은 매우 쓸모가 없었습니다.

따라서 WCF를 사용하여 View / ViewModel 애플리케이션에 별도의 데이터 모드 레이어를 추가하면 많은 작업이 추가되지만 테스트 가능성은 더 나을 수 없습니다.

도움이 되었습니까?

해결책

WOOT,이 문제를 이틀 동안 버린 후 나는 함께 살 수있는 해결책을 찾았습니다.

위의 코드 예제에서 볼 수 있듯이이 헬퍼 클래스를 사용하여 WCF 연결을 관리합니다 (Close vs Abort의 적절한 처리로 인해).

public delegate void UseServiceDelegate<T>(T proxy);

public static class Service<T>
{
  public static ChannelFactory<T> _channelFactory;

  public static void Use(UseServiceDelegate<T> codeBlock)
  {
    if (_channelFactory == null)
      _channelFactory = new ChannelFactory<T>("AlyzaServiceEndpoint");
    IClientChannel proxy = (IClientChannel)_channelFactory.CreateChannel();
    bool success = false;
    try
    {
      codeBlock((T)proxy);
      proxy.Close();
      success = true;
    }
    finally
    {
      if (!success)
      {
        proxy.Abort();
      }
    }
  }
}

내 질문에서 볼 수 있듯이 이것은이 클래스가 내 뷰 모델에서 사용되는 방식입니다.

Service<Sam.Alyza.WcfInterface.IServiceWebsites>.Use(alyzaSvc =>
{
  rc = new List<Sam.Alyza.WcfInterface.Website>(alyzaSvc.GetSites());
});

WCF 인터페이스를 조롱하려면 Mock WCF 서비스를 작성하고 호스팅해야합니다. 모든 연결 문자열을 변경하십시오. 몇 가지 테스트를 추가하기 위해 많은 작업이 있습니다.

더 쉬운 방법을 찾았습니다. 인터페이스를 구현하는 모의 서비스를 만드는 것은 간단합니다.

public class MockWebsiteService : WcfInterface.IServiceWebsites
{
  internal List<Sam.Alyza.WcfInterface.Website> _websites = new List<Sam.Alyza.WcfInterface.Website>();
  internal int _GetSitesCallCount;

  IEnumerable<Sam.Alyza.WcfInterface.Website> Sam.Alyza.WcfInterface.IServiceWebsites.GetSites()
  {
    _GetSitesCallCount++;
    return _websites;
  }
}

문제는 다음과 같습니다. 뷰 모델을 서비스 대신이 모의 클래스라고 부르는 방법은 무엇입니까?
해결책 : service.use ()는 연결을 관리합니다. 연결 관리를 무시하기 위해 기능을 추가하면 내 자신의 WCF 모의 개체를 service.use ()에 몰입 할 수 있습니다.
이를 위해서는 service.use ()를 wcf 이외의 다른 것을 호출하는 방법이 필요합니다 (#debug 섹션을보십시오).

public static class Service<T>
{
#if DEBUG
  public static T DebugOverride = default(T);
#endif
  public static ChannelFactory<T> _channelFactory;

  public static void Use(UseServiceDelegate<T> codeBlock)
  {
#if DEBUG
    if (!Object.Equals(DebugOverride, default(T)))
    {
      codeBlock(DebugOverride);
      return;
    }
#endif
    if (_channelFactory == null)
      _channelFactory = new ChannelFactory<T>("AlyzaServiceEndpoint");
    IClientChannel proxy = (IClientChannel)_channelFactory.CreateChannel();
    bool success = false;
    try
    {
      codeBlock((T)proxy);
      proxy.Close();
      success = true;
    }
    finally
    {
      if (!success)
      {
        proxy.Abort();
      }
    }
  }
}

이 테스트 후크를 서비스에 추가하면 테스트에서 T를 구현하는 객체를 몰래 넣을 수 있습니다.

MockWebsiteService mockmodel = new MockWebsiteService();
Service<WcfInterface.IServiceWebsites>.DebugOverride = mockmodel;
// run my tests here

나에게 이것은 WCF 서비스를 조롱하는 아주 좋은 방법입니다!

추신 : #IF 디버그로 인해 테스트가 릴리스 중에 컴파일되지 않음을 알고 있습니다. 당신이 돌보면 그들을 쫓아 내십시오.

다른 팁

우리는 종속성 주입을 사용 하여이 문제를 해결하여 서비스 클라이언트 (또는 서비스 클라이언트를위한 공장)를 뷰 모델에 주입합니다. 이 같은:

interface IClientFactory
{
    TClient CreateClient<TClient>();
}

class ClientFactory : IClientFactory
{
    TClient CreateClient<TClient>() 
    {
       var channelFactory = new ChannelFactory<TClient>("AlyzaServiceEndpoint");
       var proxy = (TClient)channelFactory.CreateChannel();
       return proxy;
    }
}

public ViewModel 
{
    public ViewModel(IClientFactory clientFactory)
    {
       _clientFactory = clientFactory;
    }

    private void DoWcfStuff()
    {
        using (var proxy = _clientFactory.CreateClient<IClientChannel>())
        {
           var result = proxy.GetThings();
        }
    }
}

public ViewModelTests
{
    public void Setup()
    {
       _mockFactory = new MockClientFactory();
       _viewModel = new ViewModel(_mockFactory);
    }

    [Test]
    public void Test() 
    {
       var testResult = new Result();
       var mockClient = _mockFactory.CreateClient<IClientChannel>();

       mockClient.SetResultForGetThings(testResult);

       // put the viewmodel through its paces.
    }

    private class MockClientFactory : IClientFactory
    {
        MockClient _mockClient;

        public MockClientFactory()
        {
          _mockClient = new MockClient();
        }

        public TClient CreateClient<TClient>()
        {
           if (typeof(TClient) == typeof(IClientChannel))
           {
              return _mockClient;
           }
        }
    }

    private class MockClient : IClientChannel
    {
        void SetupGetThingsResult(Result result)
        {
           _result = result;
        }

        Result GetThings() 
        {
           return _result;
        }
    }
}

핸드 코드 모의를 사용하여 예제를 보여주었습니다. 일반적으로 나는 조롱 프레임 워크와 같은 것을 사용합니다 모크.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top