Woot, после двух дней безумного обращения к этой проблеме я нашел решение, с которым я могу жить:
Как видно из приведенного выше примера кода, я использую этот вспомогательный класс для управления моим соединением WCF (из-за правильной обработки Close против 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();
}
}
}
}
Как видно из моего вопроса, этот класс используется в моей ViewModel:
Service<Sam.Alyza.WcfInterface.IServiceWebsites>.Use(alyzaSvc =>
{
rc = new List<Sam.Alyza.WcfInterface.Website>(alyzaSvc.GetSites());
});
Чтобы смоделировать интерфейс WCF, мне нужно создать и разместить фиктивную службу 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;
}
}
Единственная проблема: как заставить ViewModel вызывать этот mock-класс вместо службы?
Решение: 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();
}
}
}
}
Добавив этот тестовый хук в Service, я могу проникнуть в любой объект, реализующий T в моих тестах:
MockWebsiteService mockmodel = new MockWebsiteService();
Service<WcfInterface.IServiceWebsites>.DebugOverride = mockmodel;
// run my tests here
Для меня это очень хороший способ издеваться над сервисом WCF!
PS: я знаю, что из-за #if DEBUG тесты не будут компилироваться в релизе. Просто выгони их, если тебе не безразлично.