Screenplay pattern hoạt động như thế nào (phần 2)

Bài này về Question cách screenplay compare kết quả, có rất nhiều thứ để học tập. Nếu bạn đọc xong mà không hiểu thì bạn biết là có rất nhiều thứ bạn phải học. 😀

Chúng ta hãy bắt đầu từ 1 ví dụ đơn giản:

@Test
public void should_see_how_to_begin() {
	...
    then(james).should(seeThat(Placeholder.text(), is("What needs to be done?")));
}

I. Phân tích

  • then(james) --> james bởi vì
public static Actor then(Actor actor) { return actor; }

Như các bài trước mình đã từng nói givenThat(), when(), then() chỉ mục đích làm cho code giống câu văn.

Note cho bạn nào chưa biết: cái chữ T gọi là type parameter trong Java generics

public static <T extends PerformsTasks> T givenThat(T actor) {
    return actor;
}
public static Actor andThat(Actor actor) {return actor; }
public static Actor when(Actor actor) {  return actor; }
public static Actor then(Actor actor) { return actor; }
public static Actor and(Actor actor) { return actor; }
public static Actor but(Actor actor) { return actor; }
  • .should() sẽ thực hiện việc so sánh, nó tiếp nhận 1 hoặc nhiều object Consequence, sau đó nó sẽ forEach để so sánh từng cái một.
public final void should(Consequence... consequences) { (1)
	....
    for (Consequence consequence : consequences) {
        check(consequence, errorTally);
    }
	....
}
  • Làm thế nào để cung cấp các object này, đó là lúc các method seeThat() của class GivenWhenThen vào cuộc. Class GivenWhenThen bây giờ đóng vai trò là 1 simple Factory (bạn có thể tìm hiểu thêm về Factory Method pattern), mình viết ví dụ 2 methods thôi.
...

public static <T> Consequence<T> seeThat(Question<? extends T> actual, Matcher<T> expected){
	return new QuestionConsequence(actual, expected); (2)
}

public static <T> Consequence<T> seeThat(Question<? extends T> actual, Predicate<T> expected) {
	return new PredicateConsequence(actual, expected); (3)
}

...

Hãy nhìn vào bên trong code, mình có đánh dấu (1), (2), (3).

Note cho bạn nào chưa biết: (Consequence… consequences) gọi là varagrs

Tại sao trên mục (1), method should(Consequence… consequences) nhận Consequence mà (2) lại return QuestionConsequence và (3) return PredicateConsequence?

Vì chúng có quan hệ cha-con, “con” có thể thay thế cho “cha”, đây là 1 đặc điểm trong tính thừa kế trong java thừa kế Type.

  • Placeholder.text() sẽ tạo ra Question<T>
  • is("What needs to be done?") là Matcher của thư viện Hamcrest Matcher.

II. Đi sâu vào cách screenplay so sánh

Phần trước, chúng ta đã biết là đoạn code để thực hiện so sánh chính là

check(consequence, errorTally);

Nó sẽ gọi đến đoạn code sau đây:

private <T> void check(Consequence<T> consequence, ErrorTally errorTally) {
 	...
    consequence.evaluateFor(this); 
    ...
}

Đến đây thì nó sẽ gọi đến phần implementation method evaluateFor của từng loại Consequence theo tính chất của polymophism (đa hình).

void evaluateFor(Actor actor)

Theo ví dụ ở phía trên:

seeThat(Placeholder.text(), is("What needs to be done?"))

sẽ map vào

public static <T> Consequence<T> seeThat(Question<? extends T> actual, Matcher<T> expected) {
return new QuestionConsequence(actual, expected);
}

Do vậy, nó sẽ tạo ra QuestionConsequence. Và đây là phần implementation evaluateFor

import static org.hamcrest.MatcherAssert.assertThat;

@Override
public void evaluateFor(Actor actor) {
   ...
   assertThat(question.answeredBy(actor), expected);
   ...
}

Cuối cùng thì ta cũng thấy là thực chất thì screenplay sử dụng assertion của Hamcrest Matcher.

Ta hoàn toàn có thể viết lại test như sau mà ý nghĩa không thay đổi.

@Test
public void should_see_how_to_begin() {
	...
    assertThat(Placeholder.text().answeredBy(james), is("What needs to be done?"));
}

or

@Test
public void should_see_how_to_begin() {
	...
    String placeholderText = Placeholder.text().answeredBy(james);
    assertThat(placeholderText, is("What needs to be done?"));
}

III. Cách tạo ra question

Question<T> là 1 object mà sẽ chứa bên trong nó 1 object khác, như đã nói ở trên, Tgenerics type, có nghĩa là nó thể là object nào cũng được. Nó giống như List<T> thôi. Ví dụ:

  • Question<String>
  • Question<Integer>
  • Question<Infomation>
  • ….

Trong ví dụ trên Placeholder.text() –> Question<String>. Tất cả các class con của class Question<T> sẽ phải implement method

T answeredBy(Actor actor);

do đó, khi Placeholder.text().answeredBy(james) sẽ đươc hiểu là:

Có rất nhiều cách để tạo ra question, bạn có thể tham khảo ở đây

IV. Tổng kết

Mình đã đi vào những phần cơ bản của code bên trong thư viện screenplay, hi vọng đem thêm 1 chút thông tin cho các bạn đang muốn học và làm UI automation test mà dùng đến screenplay của framework serenity.

  • Nếu bạn thấy hay thì đừng ngại ngần cho 1 like ủng hộ tinh thần.
  • Nếu bạn không hiểu gì thì việc đọc đến những dòng này cũng thể hiện độ “lì lợm” của bạn, bạn rất xứng đáng 1 cốc nước lọc. =)))))

Bạn nào muốn nâng cấp trình độ code java, để có thể đọc hiểu phần nào code thư viện mà bạn sử dụng, hãy đăng ký khóa học Java Intermediate của mình.

Leave a Reply

Your email address will not be published. Required fields are marked *