我构建了一个连接到SQL Server数据库的.NET ASMX Web服务。有一个Web服务调用GetAllQuestions()。
var myService = new SATService();
var serviceQuestions = myService.GetAllQuestions();
我将GetAllQuestions的结果保存到本地应用程序文件夹中的GetAllQuestions.xml
有没有办法伪造Web服务调用并使用本地xml结果?
我只想获取整个sql表的内容,并为我自动生成具有相关属性名称的对象数组,就像使用LINQ to SQL Web服务一样。
请记住,我正在构建一个独立的Monotouch iPhone应用程序。
答案 0 :(得分:4)
//GetSATService returns the fake service during testing
var myService = GetSATService();
var serviceQuestions = myService.GetAllQuestions();
或者,最好在对象的构造函数中设置SATService字段(因此构造函数需要设置SATService。如果这样做,它将更容易测试。
编辑抱歉,我会在这里详细说明。上面代码中的内容是耦合依赖,代码创建它正在使用的对象。依赖注入或控制反转(IOC)模式可以让您解除依赖性。 (或者简单地说,不要称之为“新” - 让别的东西做到这一点 - 你可以在消费者之外控制的东西。)
有几种方法可以做到这一点,它们显示在下面的代码中(注释解释):
class Program
{
static void Main(string[] args)
{
//ACTUAL usage
//Setting up the interface injection
IInjectableFactory.StaticInjectable = new ConcreteInjectable(1);
//Injecting via the constructor
EverythingsInjected injected =
new EverythingsInjected(new ConcreteInjectable(100));
//Injecting via the property
injected.PropertyInjected = new ConcreteInjectable(1000);
//using the injected items
injected.PrintInjectables();
Console.WriteLine();
//FOR TESTING (normally done in a unit testing framework)
IInjectableFactory.StaticInjectable = new TestInjectable();
EverythingsInjected testInjected =
new EverythingsInjected(new TestInjectable());
testInjected.PropertyInjected = new TestInjectable();
//this would be an assert of some kind
testInjected.PrintInjectables();
Console.Read();
}
//the inteface you want to represent the decoupled class
public interface IInjectable { void DoSomething(string myStr); }
//the "real" injectable
public class ConcreteInjectable : IInjectable
{
private int _myId;
public ConcreteInjectable(int myId) { _myId = myId; }
public void DoSomething(string myStr)
{
Console.WriteLine("Id:{0} Data:{1}", _myId, myStr);
}
}
//the place to get the IInjectable (not in consuming class)
public static class IInjectableFactory
{
public static IInjectable StaticInjectable { get; set; }
}
//the consuming class - with three types of injection used
public class EverythingsInjected
{
private IInjectable _interfaceInjected;
private IInjectable _constructorInjected;
private IInjectable _propertyInjected;
//property allows the setting of a different injectable
public IInjectable PropertyInjected
{
get { return _propertyInjected; }
set { _propertyInjected = value; }
}
//constructor requires the loosely coupled injectable
public EverythingsInjected(IInjectable constructorInjected)
{
//have to set the default with property injected
_propertyInjected = GetIInjectable();
//retain the constructor injected injectable
_constructorInjected = constructorInjected;
//using basic interface injection
_interfaceInjected = GetIInjectable();
}
//retrieves the loosely coupled injectable
private IInjectable GetIInjectable()
{
return IInjectableFactory.StaticInjectable;
}
//method that consumes the injectables
public void PrintInjectables()
{
_interfaceInjected.DoSomething("Interface Injected");
_constructorInjected.DoSomething("Constructor Injected");
_propertyInjected.DoSomething("PropertyInjected");
}
}
//the "fake" injectable
public class TestInjectable : IInjectable
{
public void DoSomething(string myStr)
{
Console.WriteLine("Id:{0} Data:{1}", -10000, myStr + " For TEST");
}
}
以上是一个完整的控制台程序,您可以运行和使用它来查看其工作原理。我试着保持简单,但随时问你我有什么问题。
第二次修改: 从评论中可以看出,这是一个操作需求,而不是测试需求,所以实际上它是一个缓存。以下是一些可用于预期目的的代码。同样,下面的代码是一个完整的工作控制台程序。
class Program
{
static void Main(string[] args)
{
ServiceFactory factory = new ServiceFactory(false);
//first call hits the webservice
GetServiceQuestions(factory);
//hists the cache next time
GetServiceQuestions(factory);
//can refresh on demand
factory.ResetCache = true;
GetServiceQuestions(factory);
Console.Read();
}
//where the call to the "service" happens
private static List<Question> GetServiceQuestions(ServiceFactory factory)
{
var myFirstService = factory.GetSATService();
var firstServiceQuestions = myFirstService.GetAllQuestions();
foreach (Question question in firstServiceQuestions)
{
Console.WriteLine(question.Text);
}
return firstServiceQuestions;
}
}
//this stands in place of your xml file
public static class DataStore
{
public static List<Question> Questions;
}
//a simple question
public struct Question
{
private string _text;
public string Text { get { return _text; } }
public Question(string text)
{
_text = text;
}
}
//the contract for the real and fake "service"
public interface ISATService
{
List<Question> GetAllQuestions();
}
//hits the webservice and refreshes the store
public class ServiceWrapper : ISATService
{
public List<Question> GetAllQuestions()
{
Console.WriteLine("From WebService");
//this would be your webservice call
DataStore.Questions = new List<Question>()
{
new Question("How do you do?"),
new Question("How is the weather?")
};
//always return from your local datastore
return DataStore.Questions;
}
}
//accesses the data store for the questions
public class FakeService : ISATService
{
public List<Question> GetAllQuestions()
{
Console.WriteLine("From Fake Service (cache):");
return DataStore.Questions;
}
}
//The object that decides on using the cache or not
public class ServiceFactory
{
public bool ResetCache{ get; set;}
public ServiceFactory(bool resetCache)
{
ResetCache = resetCache;
}
public ISATService GetSATService()
{
if (DataStore.Questions == null || ResetCache)
return new ServiceWrapper();
else
return new FakeService();
}
}
希望这会有所帮助。祝你好运!
答案 1 :(得分:0)
当你说假电话时,你只是测试客户端吗?
您可以使用fiddler,拦截请求并将本地xml文件返回给客户端。不要乱用你的客户代码。
答案 2 :(得分:0)
详细说明Audie的答案
使用DI可以得到你想要的东西。很简单,您将创建一个接口,您的真实对象和模拟对象都可以实现
public interface IFoo
{}
然后你可以让你的GetSATService方法根据你的需要返回一个MockSATSerivce或真正的SATService对象。
这是您使用DI容器(某些对象存储具体类型映射的接口)的地方。您可以使用所需的类型来引导容器。因此,对于单元测试,您可以构造一个模拟容器,该容器将MockSATService注册为IFoo接口的实现者。
然后你将作为具体类型的容器但接口
IFoo mySATService = Container.Resolve<IFoo>();
然后在运行时,您只需更改容器,以便它使用运行时类型而不是模拟类型进行引导,但您的代码将保持不变(因为您将所有内容视为IFoo而不是SATService)
这有意义吗?
答案 3 :(得分:0)
随着时间的推移,我发现一种有趣的方法是通过提取接口并创建包装类。这很好地适应了IoC容器,没有一个也可以正常工作。
测试时,创建传递假服务的类。正常使用它时,只需调用空构造函数,这可能只是构造一个提供程序或使用配置文件解析它。
public DataService : IDataService
{
private IDataService _provider;
public DataService()
{
_provider = new RealService();
}
public DataService(IDataService provider)
{
_provider = provider;
}
public object GetAllQuestions()
{
return _provider.GetAllQuestions();
}
}