Поддельный вызов веб-службы ASMX - PullRequest
1 голос
/ 13 апреля 2010

Я создал веб-сервис .NET ASMX, подключающийся к базе данных SQL Server. Есть вызов веб-службы GetAllQuestions ().

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

Я сохранил результат GetAllQuestions в GetAllQuestions.xml в локальной папке приложения

Есть ли способ подделать вызов веб-службы и использовать локальный XML-результат?

Я просто хочу взять содержимое всей моей таблицы sql и автоматически создать для меня массив объектов с коррелированными именами свойств, как и в веб-службах LINQ to SQL.

Помните, что я создаю отдельное приложение Monotouch для iPhone.

Ответы [ 4 ]

4 голосов
/ 13 апреля 2010

Использование внедрение зависимостей .

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

Или, предпочтительно, в конструкторе для объекта установите поле SATService (поэтому конструктору требуется установить SATService. Если вы сделаете это, его будет проще проверить.

Редактировать: Извините, я уточню здесь. То, что у вас есть в вашем коде выше, является связанной зависимостью, где ваш код создает объект, который он использует. Внедрение зависимостей или шаблон Inversion of Control (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();
    }
}

Надеюсь, это поможет. Удачи!

0 голосов
/ 13 апреля 2010

Со временем я обнаружил, что интересный способ сделать это - извлечь интерфейс и создать класс-оболочку.Это хорошо адаптируется к контейнеру IoC, а также отлично работает без него.

При тестировании создайте класс, передающий поддельный сервис.При обычном использовании просто вызовите пустой конструктор, который может просто создать провайдера или разрешить его с помощью файла конфигурации.

    public DataService : IDataService
    {
        private IDataService _provider;

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

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

        public object GetAllQuestions()
        {
            return _provider.GetAllQuestions();
        }
    }
0 голосов
/ 13 апреля 2010

Чтобы уточнить ответ Оди

Использование DI даст вам то, что вы хотите. Очень просто вы создадите интерфейс, который ваш реальный объект и ваш фиктивный объект оба реализуют

public interface IFoo
{} 

Тогда ваш метод GetSATService возвратит либо MockSATSerivce, либо реальный объект SATService в зависимости от ваших потребностей.

Здесь вы можете использовать DI-контейнер (какой-то объект, который хранит интерфейс с конкретными сопоставлениями типов). Вы можете загрузить контейнер с нужными вам типами. Таким образом, для модульного теста вы можете создать фиктивный контейнер, который регистрирует MockSATService в качестве реализации интерфейса IFoo.

Тогда вы бы как контейнер для конкретного типа, но интерфейс

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

Тогда во время выполнения вы просто измените контейнер так, чтобы он загружался с типами времени выполнения вместо типов mock, но ваш код остался бы прежним (потому что вы рассматриваете все как IFoo вместо SATService)

Имеет ли это смысл?

0 голосов
/ 13 апреля 2010

когда вы говорите «фальшивый звонок», вы просто тестируете клиентскую сторону?

Вы можете использовать fiddler, перехватить запрос и вернуть клиенту локальный XML-файл. Тогда не стоит возиться с кодом вашего клиента.

...