Как применить синхронизацию? - PullRequest
2 голосов
/ 15 января 2012

У меня есть тесты автоматизации пользовательского интерфейса.В тестах участвуют три объекта -

Класс объекта данных - данные, которые должны быть заполнены в формах.Здесь каждая форма на странице может быть представлена ​​отдельным объектом данных.
Вспомогательный класс - который заполняет данные в форме на странице Test class - который использует объект данных и вспомогательный класс для выполнения теста.

Ниже приведена сокращенная версия теста -

public class ParallelDataObject {

    HelperClass helperClass = new HelperClass();
    Data data;

    @BeforeMethod
    public void setTestData() {
        data = new Data();
        helperClass.setData(data);
    }

    @Test
    public void passM1() {
        helperClass.verifyFlag();
    }

    @Test
    public void failM2() {
        data.setFlag(false);
        helperClass.setData(data);
        helperClass.verifyFlag();
    }

    @Test
    public void passM3() {
        helperClass.verifyFlag();
    }

    @Test
    public void failM4() {
        data.setFlag(false);
        helperClass.setData(data);
        helperClass.verifyFlag();
    }
}

class HelperClass {
    Data data;  

    public void setData(Data data) {
        synchronized (data) {
            this.data = data;
        }
    }

    public void verifyFlag() {
        synchronized (data) {
            assert data.getFlag();
        }
    }
}

class Data {
    private boolean flag;

    public Data() {
        flag = true;
    }

    public Data setFlag(boolean flag) {
        synchronized (this) {
            this.flag = flag;
            return this;
        }
    }

    public boolean getFlag() {
        synchronized (this) {
            return flag;
        }
    }

При параллельном выполнении методов я обнаружил странные результаты, так как данные не являются потокобезопасными.Затем я включил синхронизировать блоки, но все же я получаю странные результаты.Я уверен, что я напутал, как синхронизация должна использоваться здесь. Любое понимание?

Я сделал еще одно упражнение.Я настроил другой тестовый класс точно так же, как первый тестовый класс.Я удалил всю синхронизацию из помощника и класса данных.Когда я запускаю классы параллельно (вместо методов).Результаты теста соответствуют ожиданиям.Почему я не запускаю параллелизм, когда выполняю классы параллельно, даже если они используют один и тот же вспомогательный класс и объект данных?

Ответы [ 2 ]

2 голосов
/ 16 января 2012

HelperClass и Data являются поточно-ориентированными.

Проблема в том, что некоторые из ваших методов тестирования выполняют несколько операций.И последовательность операций в тестовом методе не атомарна, пока она не синхронизирована.

Например, во время выполнения failM4 состояние helperClass может быть изменено другим потоком.

Я бы порекомендовал вам не использовать общее состояние между методами тестирования, поскольку синхронизация сведет на нетПреимущества одновременного выполнения тестов.

1 голос
/ 16 января 2012

Рассмотрите возможность использования ThreadLocal. Таким образом, каждый поток имеет свою собственную копию HelperClass. Обратите внимание, что синхронизация отдельных методов ничего не даст - изменения, внесенные в одном тесте (в одном потоке), видны в других тестах

class ParallelDataObject {

    private final ThreadLocal<HelperClass> helperClassThreadLocal = new ThreadLocal<HelperClass>() {

        @Override
        protected HelperClass initialValue() {
            return new HelperClass(new Data());
        }
    };

    private HelperClass helperClass() {
        return helperClassThreadLocal.get();
    }

    @Test
    public void passM1() {
        helperClass().verifyFlag();
    }

    @Test
    public void failM2() {
        helperClass().getData().setFlag(false);
        helperClass().verifyFlag();
    }

}

class HelperClass {

    private final Data data;

    public HelperClass(Data data) {
        this.data = data;
    }

    public Data getData() {
        return data;
    }

    public void verifyFlag() {
        assert data.getFlag();
    }
}

class Data {
    private boolean flag = true;

    public Data setFlag(boolean flag) {
        this.flag = flag;
        return this;
    }

    public boolean getFlag() {
        return flag;
    }
}

Другие улучшения:

  • passM3 и failM4 были лишними
  • , поскольку HelperClass для работы требуется экземпляр Data, он должен объявить его с помощью зависимости конструктора
  • при использовании:

    synchronized(this)
    

    Оборачивая все тело метода, рассмотрите возможность использования ключевого слова synchronized в объявлении метода (более читабельно).

  • синхронизация больше не требуется с ThreadLocal s

Проверка безгражданства

@ gpeche дает хорошее предположение, что тесты должны быть независимыми. К сожалению (почему, о, почему !?) JUnit повторно использует один и тот же экземпляр класса тестового примера (ParallelDataObject в этом случае) для выполнения всех тестовых методов. Это означает, что назначение любых объектов с состоянием для полей класса тестового случая опасно, и его следует избегать.

В этом конкретном случае OP должен был бы создать новый экземпляр HelperClass в каждом методе тестирования (что, на самом деле, неплохая идея) :

class ParallelDataObject {

    @Test
    public void passM1() {
        final HelperClass helperClass = new HelperClass(new Data());

        helperClass.verifyFlag();
    }

    @Test
    public void failM2() {
        final Data data = new Data();
        data.setFlag(false);

        final HelperClass helperClass = new HelperClass(data);

        helperClass.verifyFlag();
    }

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