Разделы портала

Онлайн-тренинги

.
Принципы SOLID для тестировщиков: принцип разделения интерфейсов
10.09.2024 00:00

Автор: Кристин Джеквони (Kristin Jackvony)
Оригинал статьи
Перевод: Ольга Алифанова

Мы уже изучили больше половины принципов SOLID! Сегодня настало время узнать про букву I: принцип разделения интерфейсов.

Чтобы понять этот принцип, надо сначала разобраться, что такое интерфейс. Интерфейс – это определение набора методов, который можно реализовать в классе. Каждый класс с реализацией интерфейса должен использовать все включенные в интерфейс методы. Так как интерфейс определяет только подпись метода (имя, параметры и тип возвращаемого значения), методы могут варьировать в разных реализациях.

Принцип разделения интерфейсов гласит, что класс не должен быть принужден зависеть от методов, которыми он не пользуется. Чтобы с этим разобраться, возьмем пример. Представьте, что вы тестируете сайт, на котором две разных формы – форма сотрудника и форма работодателя. Вы хотите создать интерфейс Form с методами для взаимодействия с объектами, присутствующими на форме.

interface Form {
fillInTextField(id: String, value: String): void;
selectRadioButton(id: String): void;
checkCheckbox(id: String): void;
}

Создавая класс EmployeeForm, вы реализуете интерфейс Form:

class EmployeeForm implements Form {
    fillInTextField(id: String, value: String): void {
    driver.findElement(By.id(id)).sendKeys(value)
    }
 
    selectRadioButton(id: String): void {
    driver.findElement(By.id(id)).click()
    }
 
    checkCheckbox(id: String): void {
    driver.findElement(By.id(id)).click()
    }
}

Это отлично работает – в форме сотрудника есть текстовые поля, радиокнопки и чекбоксы.

Затем вы создаете класс EmployerForm, тоже реализующий интерфейс Form. Но в этой форме только текстовые поля, а радиокнопки и чекбоксы отсутствуют. Вы реализуете интерфейс вот так:

class EmployerForm implements Form {
fillInTextField(id: String, value: String): void {
driver.findElement(By.id(id)).sendKeys(value)
}
 
selectRadioButton(id: String): void {
// нет радиокнопки
throw new Error("No radio button exists");
}
 
checkCheckbox(id: String): void {
// нет чекбокса
throw new Error("No checkbox exists");
}
}

Вы никогда не будете вызывать методы selectRadioButton и checkCheckbox в классе EmployerForm, потому что там не существует ни радиокнопок, ни чекбоксов, но вам все равно нужно создавать методы для них, так как вы реализуете интерфейс. Это нарушает принцип разделения интерфейсов.

Как же использовать интерфейсы в этих формах, не нарушая принцип? Можно создать разные интерфейсы для текстовых полей, радиокнопок и чекбоксов:

interface TextField {
fillInTextField(id: String, value: String): void;
}
 
interface RadioButton {
selectRadioButton(id: String): void;
}
 
interface Checkbox {
checkCheckbox(id: String): void;
}

Затем, создавая класс EmployeeForm, вы внедряете три интерфейса:

class EmployeeForm implements TextField, RadioButton, Checkbox {
fillInTextField(id: String, value: String): void {
driver.findElement(By.id(id)).sendKeys(value)
}
 
selectRadioButton(id: String): void {
driver.findElement(By.id(id)).click()
 
}
 
checkCheckbox(id: String): void {
driver.findElement(By.id(id)).click()
}
}

Создавая класс EmployerForm, вы внедрите только интерфейс TextField:

class EmployerForm implements TextField {
fillInTextField(id: String, value: String): void {
driver.findElement(By.id(id)).sendKeys(value)
}
}

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

Обсудить в форуме