WatiN Распараллеливание внутри теста - PullRequest
1 голос
/ 17 мая 2011

У меня есть тест, в котором я хочу убедиться, что отдельные операции на одной странице приводят к разным результатам.В частности, у меня есть несколько способов сортировки на странице, и я хочу тест, чтобы убедиться, что каждый вид сортировки отличается.У меня есть другие тесты, чтобы убедиться в правильности каждого вида.

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

Пусть «generateHashFromSearchResults ()» - это функция, которая возвращает строку, представляющую порядок результатов поиска, отображаемых в текущемIE экземпляр.Вот как выглядит рабочий код в сериализованном виде с использованием одного экземпляра браузера:

var set = new HashSet<string>();
var sortOptions = new List<String>() { "sort1", "sort2", "sort3" };

// Default sort
set.Add(generateHashFromSearchResults());

sortOptions.ForEach(s => {
  ie.Link(Find.ByText(s)).Click();
  set.Add(generateHashFromSearchResults());
});

Assert.That(set.Count() == 4);

Я читал о PLINQ несколько месяцев назад и подумал, что это может быть приличным вариантом использования.Теперь пусть «generateHashFromSearchResults (IE ie)» будет той же функцией, но она работает с явно определенным экземпляром IE.Я попробовал что-то вроде этого:

List<string> resultList = sortOptions.AsParallel().Select(s => {
  var ie = new IE(true);
  ie.Link(Find.ByText(s)).Click();
  return generateHashFromSearchResults(ie);
}).ToList();

// Forget about default sort for now. There should be 3 distinct results
Assert.That(new HashSet<string>(resultList).Count() == 3);

Самая большая проблема, с которой я сталкиваюсь сейчас, это не понимание того, как PLINQ управляет потоками.WatiN должен работать с состоянием квартиры, установленным в однопоточное состояние (STAThread).Я получаю, что каждый экземпляр IE должен быть в своем собственном потоке, но никакое количество установки каждого потока в запросе PLINQ на правильное состояние квартиры не решает проблему.

Я начинаю подозревать, что мне либо нужночтобы продолжить, узнайте больше о PLINQ или о том, что мне нужно больше узнать об управлении потоками вручную.

Есть мысли?

Ответы [ 2 ]

2 голосов
/ 07 июня 2011

Нельзя указать собственный планировщик с помощью AsParallel (). Но вы можете создать задачу для каждой опции сортировки и передать экземпляр пользовательского планировщика в метод Start (). Эта реализация планировщика потока STA была заимствована у Стивена Туба (http://blogs.msdn.com/b/pfxteam/archive/2010/04/07/9990421.aspx):

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

/// <summary>
///     Provides a scheduler that uses STA threads.
///     Borrowed from Stephen Toub's implementation http://blogs.msdn.com/b/pfxteam/archive/2010/04/07/9990421.aspx
/// </summary>
public sealed class StaTaskScheduler : TaskScheduler, IDisposable
{
    /// <summary>
    ///     The STA threads used by the scheduler.
    /// </summary>
    private readonly List<Thread> threads;

    /// <summary>
    ///     Stores the queued tasks to be executed by our pool of STA threads.
    /// </summary>
    private BlockingCollection<Task> tasks;

    /// <summary>
    ///     Initializes a new instance of the StaTaskScheduler class with the specified concurrency level.
    /// </summary>
    /// <param name = "numberOfThreads">The number of threads that should be created and used by this scheduler.</param>
    public StaTaskScheduler(int numberOfThreads)
    {
        if (numberOfThreads < 1)
        {
            throw new ArgumentOutOfRangeException(
                "numberOfThreads", "The scheduler must create at least one thread");
        }

        // Initialize the tasks collection
        this.tasks = new BlockingCollection<Task>();

        // Create the threads to be used by this scheduler
        this.threads = Enumerable.Range(0, numberOfThreads).Select(
            i =>
                {
                    var thread = new Thread(
                        () =>
                            {
                                // Continually get the next task and try to execute it.
                                // This will continue until the scheduler is disposed and no more tasks remain.
                                foreach (Task t in this.tasks.GetConsumingEnumerable())
                                {
                                    this.TryExecuteTask(t);
                                }
                            }) {
                                  Name = "Sta Thread", IsBackground = true 
                               };
                    thread.SetApartmentState(ApartmentState.STA);
                    return thread;
                }).ToList();

        // Start all of the threads
        this.threads.ForEach(t => t.Start());
    }

    /// <summary>
    ///     Gets the maximum concurrency level supported by this scheduler.
    /// </summary>
    public override int MaximumConcurrencyLevel
    {
        get
        {
            return this.threads.Count;
        }
    }

    /// <summary>
    ///     Cleans up the scheduler by indicating that no more tasks will be queued.
    ///     This method blocks until all threads successfully shutdown.
    /// </summary>
    public void Dispose()
    {
        if (this.tasks != null)
        {
            // Indicate that no new tasks will be coming in
            this.tasks.CompleteAdding();

            // Wait for all threads to finish processing tasks
            foreach (Thread thread in this.threads)
            {
                thread.Join();
            }

            // Cleanup
            this.tasks.Dispose();
            this.tasks = null;
        }
    }

    /// <summary>
    ///     Provides a list of the scheduled tasks for the debugger to consume.
    /// </summary>
    /// <returns>An enumerable of all tasks currently scheduled.</returns>
    protected override IEnumerable<Task> GetScheduledTasks()
    {
        // Serialize the contents of the blocking collection of tasks for the debugger
        return this.tasks.ToArray();
    }

    /// <summary>
    ///     Queues a Task to be executed by this scheduler.
    /// </summary>
    /// <param name = "task">The task to be executed.</param>
    protected override void QueueTask(Task task)
    {
        // Push it into the blocking collection of tasks
        this.tasks.Add(task);
    }

    /// <summary>
    ///     Determines whether a Task may be inlined.
    /// </summary>
    /// <param name = "task">The task to be executed.</param>
    /// <param name = "taskWasPreviouslyQueued">Whether the task was previously queued.</param>
    /// <returns>true if the task was successfully inlined; otherwise, false.</returns>
    protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
    {
        // Try to inline if the current thread is STA
        return Thread.CurrentThread.GetApartmentState() == ApartmentState.STA && this.TryExecuteTask(task);
    }
}
1 голос
/ 19 мая 2011

Может быть, вам следует использовать Task Parallel Library?

Я новичок в TPL, но есть планировщики, у которых, возможно, есть некоторые параметры для установки STAThread для запланированных задач.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...