Как мне создать прокси-объект, который перенаправляет вызовы методов в c #?

#c# #unit-testing #selenium

#c# #модульное тестирование #selenium

Вопрос:

Я пытаюсь использовать selenium для запуска тестов; похоже, что нет хорошего способа запустить один и тот же набор модульных тестов в нескольких браузерах.

Я прочитал этот пост о параллельном выполнении тестов: http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet_19.html

Однако я использую платформу модульного тестирования Visual studio.

Я могу создать прокси-класс, подобный этому:

 public class SeleniumProxy {
    private List<DefaultSelenium> targets;
    public SeleniumProxy() {
        targets = new List<DefaultSelenium>();
        targets.Add(new DefaultSelenium(... "firefox"...));
        targets.Add(new DefaultSelenium(... "iexplore"...));
    }
    public void Open(String url) {
        foreach (var i in targets) {
            i.Open(url);
        }
    }
    ...
}
  

Мой вопрос заключается в следующем? Как я могу это сделать, не переписывая весь класс в качестве прокси?

Я подумал, может быть, передать lamda в аргументы map или передать функцию, которая принимает имя вызываемого метода, но все это кажется довольно неубедительными идеями.

Чего я действительно хочу, так это добавить элемент, подобный:

 public class SeleniumProxy {
    public dynamic proxy;
    ....
}
  

И вызвать это следующим образом:

 var selenium = getProxy();
selenium.proxy.Open("...");
  

Допускает ли c # такой синтаксис для динамических объектов?

Или какой-то мета-обработчик для классов, который позволяет им перехватывать исключения, не относящиеся к такому методу, и обрабатывать их вручную?

В принципе: как я могу создать прокси-объект, который динамически вызывает методы для внутреннего члена класса?

(Редактировать: возможно … используя отражение в объекте DefaultSelenium и создавая заглушки функций в динамическом прокси-объекте для каждой записи ..?)

Комментарии:

1. На вашем месте я бы снова заглянул в Selenium Grid, он довольно продвинутый. По моему опыту, тестирование IE Selenium занимает в 5-10 раз больше времени, чем FF, поэтому, если вы попытаетесь запускать тесты параллельно по-своему, ваши тесты FF также будут длиться вечно.

2. На самом деле не имеет значения, займет ли это больше времени; вам все равно придется подождать IE.

Ответ №1:

Если я понимаю, что вы пытаетесь, я думаю, вы можете расширить DynamicObject для достижения этой цели.

 class Proxy : System.Dynamic.DynamicObject
{
    public Proxy(object someWrappedObject) { ... }

    public override bool TryInvokeMember(System.Dynamic.InvokeMemberBinder binder, object[] args, out object result)
    {
      // Do whatever, binder.Name will be the called method name
    }
}
  

//Do whatever... стал бы некоторым кодом, который обращается к внутренним элементам какого-либо другого объекта (предположительно, посредством отражения), используя binder.Name как часть процесса поиска.

Существуют TryGetMember и TryGetIndex методы, которые можно переопределить, если вам нужно обернуть что-нибудь более причудливое, что вызывает простой метод.

Вам придется приводить экземпляры Proxy в dynamic после построения, чтобы выполнять произвольные вызовы, точно так же, как при работе с ExpandoObject.

Ответ №2:

Вы могли бы использовать наследование и определить свои тесты в абстрактном базовом классе с фабричным методом для создания вашего экземпляра selenium, а затем наследовать это для каждого типа браузера, который вы хотите смоделировать. Затем тесты будут выполняться для каждого унаследованного класса с помощью соответствующего браузера. Используя NUnit в качестве примера:

 public abstract class AbstractTests
{
    protected abstract DefaultSelenium CreateSelenium();

    [Test]
    public void TestSomethingA()
    {
        DefaulSelenium selenium = CreateSelenium();

        //Do some testing with selenium.
    }
}

[TestFixture]
public class IETests : AbstractTests
{
    protected override DefaultSelenium CreateSelenium()
    {
        return new DefaultSelenium("iexplore");
    }
}

[TestFixture]
public class FirefoxTests : AbstractTests
{
    protected override DefaultSelenium CreateSelenium()
    {
        return new DefaultSelenium("firefox");
    }
}