代码之家  ›  专栏  ›  技术社区  ›  Bryan

假ASMX Web服务调用

  •  1
  • Bryan  · 技术社区  · 15 年前

    我建立了一个连接到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服务一样。

    请记住,我正在构建一个独立的单触式iPhone应用程序。

    4 回复  |  直到 15 年前
        1
  •  4
  •   Audie    15 年前

    使用 dependency injection .

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

    希望这有帮助。祝你好运!

        2
  •  0
  •   Stephen Dolier    15 年前

    当你说假电话,你只是在测试客户端吗?

    您可以使用fiddler,截获请求并将本地XML文件返回给客户机。那就不要乱弄你的客户代码了。

        3
  •  0
  •   Brad Cunningham    15 年前

    详细说明奥迪的回答

    使用DI可以得到你想要的。很简单,您将创建一个接口,您的真实对象和模拟对象都可以实现该接口。

    public interface IFoo
    {} 
    

    然后让getsatservice方法根据需要返回mocksatserivce或真正的satservice对象。

    在这里,您将使用一个DI容器(一些存储具体类型映射接口的对象),用您想要的类型引导容器。因此,对于单元测试,您可以构造一个模拟容器,将mocksatservice注册为ifoo接口的实现者。

    然后您将作为具体类型but接口的容器

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

    然后在运行时,您只需更改容器,使其使用运行时类型而不是模拟类型进行引导,但您的代码将保持不变(因为您将所有内容都视为ifoo而不是satservice)

    这有道理吗?

        4
  •  0
  •   Haas    15 年前

    随着时间的推移,我发现这样做的一个有趣的方法是提取一个接口并创建一个包装类。这很适合IOC容器,而且没有容器也可以正常工作。

    测试时,创建通过假服务的类。当正常使用它时,只需调用空构造函数,它可以简单地构造一个提供程序或使用配置文件解析一个提供程序。

        public DataService : IDataService
        {
            private IDataService _provider;
    
            public DataService()
            {
                _provider = new RealService();
            }
    
            public DataService(IDataService provider)
            {
                _provider = provider;
            }
    
            public object GetAllQuestions()
            {
                return _provider.GetAllQuestions();
            }
        }