简体   繁体   English

假ASMX Web服务调用

[英]Fake ASMX Web Service Call

I built a .NET ASMX web service connecting to an SQL Server database. 我构建了一个连接到SQL Server数据库的.NET ASMX Web服务。 There is a web service call GetAllQuestions(). 有一个Web服务调用GetAllQuestions()。

 var myService = new SATService();
 var serviceQuestions = myService.GetAllQuestions();

I saved the result of GetAllQuestions to GetAllQuestions.xml in the local application folder 我将GetAllQuestions的结果保存到本地应用程序文件夹中的GetAllQuestions.xml

Is there any way to fake the web service call and use the local xml result? 有没有办法伪造Web服务调用并使用本地xml结果?

I just want to take the contents of my entire sql table and have the array of objects with correlating property names automatically generated for me just like with LINQ to SQL web services. 我只想获取整个sql表的内容,并为我自动生成具有相关属性名称的对象数组,就像使用LINQ to SQL Web服务一样。

Please keep in mind that I am building a standalone Monotouch iPhone application. 请记住,我正在构建一个独立的Monotouch iPhone应用程序。

Use dependency injection . 使用依赖注入

//GetSATService returns the fake service during testing     
var myService = GetSATService(); 
var serviceQuestions = myService.GetAllQuestions();

Or, preferably, in the constructor for the object set the SATService field (so the constructor requires the SATService to be set. If you do this, it will be easier to test. 或者,最好在对象的构造函数中设置SATService字段(因此构造函数需要设置SATService。如果这样做,它将更容易测试。

Edit: Sorry, I'll elaborate here. 编辑:对不起,我在这里详细说明。 What you have in your code above is a coupled dependency, where your code creates the object it is using. 上面代码中的内容是耦合依赖,代码创建它正在使用的对象。 Dependency injection or the Inversion of Control(IOC) pattern, would have you uncouple that dependency. 依赖注入或控制反转(IOC)模式可以让您解除依赖性。 (Or simply, don't call "new" - let something else do that - something you can control outside the consumer.) (或者简单地说,不要称之为“新” - 让别的东西做到这一点 - 你可以在消费者之外控制的东西。)

There are several ways to do this, and they are shown in the code below (comments explain): 有几种方法可以做到这一点,它们显示在下面的代码中(注释说明):

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

The above is a complete console program that you can run and play with to see how this works. 以上是一个完整的控制台程序,您可以运行和使用它来查看其工作原理。 I tried to keep it simple, but feel free to ask me any questions you have. 我试着保持简单,但随时问你我有什么问题。

Second Edit: From the comments, it became clear that this was an operational need, not a testing need, so in effect it was a cache. 第二次编辑:从评论中可以看出,这是一个操作需求,而不是测试需求,所以实际上它是一个缓存。 Here is some code that will work for the intended purpose. 以下是一些可用于预期目的的代码。 Again, the below code is a full working console program. 同样,下面的代码是一个完整的工作控制台程序。

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

Hope this helps. 希望这可以帮助。 Good luck! 祝好运!

when you say fake the call, are you just testing the client side? 当你说假电话时,你只是测试客户端吗?

you could use fiddler, intercept the request and return the local xml file to the client. 您可以使用fiddler,拦截请求并将本地xml文件返回给客户端。 No messing around with your client code then. 不要乱用你的客户端代码。

To elaborate on Audie's answer 详细说明奥迪的答案

Using DI would get you what you want. 使用DI可以得到你想要的东西。 Very simply you would create an interface that your real object and your mock object both implement 很简单,您将创建一个您的真实对象和模拟对象都实现的接口

public interface IFoo
{} 

Then you would have your GetSATService method return either a MockSATSerivce or the real SATService object based on your needs. 然后,您可以让GetSATService方法根据您的需要返回MockSATSerivce或真正的SATService对象。

This is where you would use a DI container (some object that stores interface to concrete type mappings) You would bootstrap the container with the types you want. 在这里你可以使用一个DI容器(一些存储接口到具体类型映射的对象)你可以使用你想要的类型来引导容器。 So, for a unit test, you could contrstruct a mock container that registers the MockSATService as the implementer of the IFoo interface. 因此,对于单元测试,您可以构造一个模拟容器,该容器将MockSATService注册为IFoo接口的实现者。

Then you would as the container for the concrete type but interface 然后你将作为具体类型但接口的容器

IFoo mySATService = Container.Resolve<IFoo>();

Then at runtime you would just change out the container so that it bootstraps with the runtime types instead of the mock types but you code would stay the same (Because you are treating everything as IFoo instead SATService) 然后在运行时,您只需更改容器,以便它使用运行时类型而不是模拟类型进行引导,但您的代码将保持不变(因为您将所有内容都视为IFoo而不是SATService)

Does that make sense? 那有意义吗?

Over time I found that an interesting way to do this is by extracting an interface and creating a wrapper class. 随着时间的推移,我发现一种有趣的方法是通过提取接口并创建包装类。 This adapts well to a IoC container and also works fine without one. 这很好地适应了IoC容器,没有一个也可以正常工作。

When testing, create the class passing a fake service. 测试时,创建传递虚假服务的类。 When using it normally, just call the empty constructor, which might simply construct a provider or resolve one using a config file. 正常使用它时,只需调用空构造函数,这可能只是构造一个提供程序或使用配置文件解析它。

    public DataService : IDataService
    {
        private IDataService _provider;

        public DataService()
        {
            _provider = new RealService();
        }

        public DataService(IDataService provider)
        {
            _provider = provider;
        }

        public object GetAllQuestions()
        {
            return _provider.GetAllQuestions();
        }
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM