Nội dung bài học
1. Take Screenshot khi chạy test case
Chụp ảnh màn hình lại để khi mình chạy Pass hay Fail sẽ có cái nhìn trực quan lưu lại được từng case xử lý và cả record video nữa.
Đầu tiên nghiên cứu đoạn code Take Screenshot trước nè:
// Tạo tham chiếu của TakesScreenshot với driver hiện tại
TakesScreenshot ts = (TakesScreenshot) driver;
// Gọi hàm capture screenshot - getScreenshotAs
File source = ts.getScreenshotAs(OutputType.FILE);
//Kiểm tra folder tồn tại. Nêu không thì tạo mới folder
File theDir = new File("./Screenshots/");
if (!theDir.exists()) {
theDir.mkdirs();
}
// result.getName() lấy tên của test case xong gán cho tên File chụp màn hình luôn
FileHandler.copy(source, new File("./Screenshots/" + result.getName() + ".png"));
System.out.println("Screenshot taken: " + result.getName());
TakesScreenshot ts = (TakesScreenshot) driver; Cái TakesScreenshot
này thuộc Selenium nhen
// Gọi hàm capture screenshot là getScreenshotAs
File source = ts.getScreenshotAs(OutputType.FILE);
Hàm FileHandler.copy
để sao chép source dạng file ở trên vào đường dẫn khai báo trong hàm File()
với đuôi dạng hình ảnh như: png, jpg, jpeg.
Còn cái result
từ đâu ra. Nó là lớp ITestResult là một giao diện Interface của TestNG hỗ trợ.
Gọi nó lại bằng cách bỏ vào trực tiếp tham số trong hàm với đối tượng như khai báo class
@Test
public void homePage(ITestResult result) throws Exception {
driver.get("https://anhtester.com");
//step này fail sẽ chụp màn hình lại
assertEquals(driver.getTitle(), "Anh Tester - Automation Test");
//Code chụp màn hình bên trên vào đây
}
Với đối tượng result thuộc giao diện ITestResult thì chúng ta có các hàm hỗ trợ sau:
Hiện tại chúng ta lấy cái Name của Test case dùng hàm getName()
hiện tại thôi để đặt tên cho ảnh chụp dễ hình dung là ở page nào và step gì.
Lúc này code đầy đủ như này:
@Test
public void homePage(ITestResult result) throws Exception {
driver.get("https://anhtester.com");
assertEquals(driver.getTitle(), "Anh Tester - Automation Test");
// Chụp màn hình step này lại
// Tạo tham chiếu của TakesScreenshot
TakesScreenshot ts = (TakesScreenshot) driver;
// Gọi hàm capture screenshot - getScreenshotAs
File source = ts.getScreenshotAs(OutputType.FILE);
//Kiểm tra folder tồn tại. Nêu không thì tạo mới folder
File theDir = new File("./Screenshots/");
if (!theDir.exists()) {
theDir.mkdirs();
}
// result.getName() lấy tên của test case xong gán cho tên File chụp màn hình
FileHandler.copy(source, new File("./Screenshots/" + result.getName() + ".png"));
System.out.println("Đã chụp màn hình: " + result.getName());
}
(Không thích thì ghi trực tiếp tên nào vào cũng được nhé. Không cần dùng ITestResult)
Nó lưu file có tên là "homePage" tên test step và lưu vào thư mục "Screenshots"
Tạo sẵn thư mục cũng được hoặc nếu không tạo trước thì nó chạy xong tự tạo sinh ra luôn.
Vậy là xong rồi. Thử đi nào.
Rồi bây giờ làm cách nào để chỉ chụp những màn hình với step bị Faill hoặc đã Pass
Cách chụp ảnh màn hình cho những trường hợp Fail
Chúng ta nhớ lại cái @AfterMethod chứ hả. Nó sẽ chạy sau mỗi @Test. Và nó đó, chính là nó chứ không ai =))
Mình kết hợp nó với ITestResult trên đã nói để thiết lặp trường hợp là sau mỗi lần chạy xong @Test thì check trạng thái để chụp hình theo trạng thái đặt điều kiện tùy ý.
Dô code luôn nè
// Nó sẽ thực thi sau mỗi lần thực thi testcase (@Test)
@AfterMethod
public void takeScreenshot(ITestResult result) {
// Khởi tạo đối tượng result thuộc ITestResult để lấy trạng thái và tên của từng Step
// Ở đây sẽ so sánh điều kiện nếu testcase passed hoặc failed
// passed = SUCCESS và failed = FAILURE
if (ITestResult.FAILURE == result.getStatus()) {
try {
// Tạo tham chiếu của TakesScreenshot
TakesScreenshot ts = (TakesScreenshot) driver;
// Gọi hàm capture screenshot - getScreenshotAs
File source = ts.getScreenshotAs(OutputType.FILE);
//Kiểm tra folder tồn tại. Nêu không thì tạo mới folder
File theDir = new File("./Screenshots/");
if (!theDir.exists()) {
theDir.mkdirs();
}
// result.getName() lấy tên của test case xong gán cho tên File chụp màn hình
FileHandler.copy(source, new File("./Screenshots/" + result.getName() + ".png"));
System.out.println("Đã chụp màn hình: " + result.getName());
} catch (Exception e) {
System.out.println("Exception while taking screenshot " + e.getMessage());
}
}
}
Cái thằng ITestResult.FAILURE là lấy trạng thái gốc của giao diện hỗ trợ so sánh với trạng thái của Test case trả về từ Result
Rồi thôi vậy là xong.
Code mẫu để chụp ảnh màn hình cho 2 cái @Test
package anhtester.com.testcases;
import io.github.bonigarcia.wdm.WebDriverManager;
import org.openqa.selenium.By;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.io.FileHandler;
import org.testng.Assert;
import org.testng.ITestResult;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.io.File;
import java.util.concurrent.TimeUnit;
public class CaptureScreenshot {
private WebDriver driver;
@BeforeClass
public void setup() {
WebDriverManager.chromedriver().setup();
driver = new ChromeDriver();
driver.manage().timeouts().pageLoadTimeout(20, TimeUnit.SECONDS);
driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);
driver.manage().window().maximize();
}
@Test(priority = 1)
public void homePage() throws Exception {
driver.get("https://anhtester.com");
//step này cố tình Fail để chụp màn hình lại
Assert.assertEquals(driver.getTitle(), "Anh Tester - Automation Test");
}
@Test(priority = 2)
public void loginPage() throws Exception {
driver.findElement(By.id("btn-login")).click();
}
// Nó sẽ thực thi sau mỗi lần thực thi testcase (@Test)
@AfterMethod
public void takeScreenshot(ITestResult result) throws InterruptedException {
Thread.sleep(1000);
//Khởi tạo đối tượng result thuộc ITestResult để lấy trạng thái và tên của từng Test Case
//Ở đây sẽ so sánh điều kiện nếu testcase passed hoặc failed
//passed = SUCCESS và failed = FAILURE
if (ITestResult.FAILURE == result.getStatus()) {
try {
// Tạo tham chiếu của TakesScreenshot
TakesScreenshot ts = (TakesScreenshot) driver;
// Gọi hàm capture screenshot - getScreenshotAs
File source = ts.getScreenshotAs(OutputType.FILE);
//Kiểm tra folder tồn tại. Nêu không thì tạo mới folder
File theDir = new File("./Screenshots/");
if (!theDir.exists()) {
theDir.mkdirs();
}
// result.getName() lấy tên của test case xong gán cho tên File chụp màn hình
FileHandler.copy(source, new File("./Screenshots/" + result.getName() + ".png"));
System.out.println("Đã chụp màn hình: " + result.getName());
} catch (Exception e) {
System.out.println("Exception while taking screenshot " + e.getMessage());
}
}
}
@AfterClass
public void tearDown() throws Exception {
Thread.sleep(1000);
driver.quit();
}
}
Lúc này cái @Test của homePage sẽ Fail (vì title không đúng) nên nó chỉ chụp ảnh của homePage. Chứ nó mà sinh ra thêm tấm của loginPage thì thấy bà luôn =))
Rồi xong rồi đó
Nếu mà muốn viết hàm riêng để gọi lại từ class nào đó thì làm như mấy bài trước thôi
Ví dụ tạo class là captureHelpers và code vào thôi
package anhtester.com.common.helpers;
import anhtester.com.common.ultilities.PropertiesFile;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.io.FileHandler;
import org.testng.Reporter;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class CaptureHelpers {
//Lấy đường dẫn đến project hiện tại
static String projectPath = System.getProperty("user.dir") + "/";
//Tạo format ngày giờ để xíu gắn dô cái name của screenshot hoặc record video
private static SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH-mm-ss");
public static void captureScreenshot(WebDriver driver, String screenName) {
PropertiesFile.setPropertiesFile();
try {
Reporter.log("Driver for Screenshot: " + driver);
// Tạo tham chiếu đối tượng của TakesScreenshot với dirver hiện tại
TakesScreenshot ts = (TakesScreenshot) driver;
// Gọi hàm getScreenshotAs để chuyển hóa hình ảnh về dạng FILE
File source = ts.getScreenshotAs(OutputType.FILE);
//Kiểm tra folder nếu không tồn tại thì tạo folder
File theDir = new File(projectPath + PropertiesFile.getPropValue("exportCapturePath"));
if (!theDir.exists()){
theDir.mkdirs();
}
// Chổ này đặt tên thì truyền biến "screenName" gán cho tên File chụp màn hình
FileHandler.copy(source, new File(projectPath + PropertiesFile.getPropValue("exportCapturePath") + "/" + screenName + "_" + dateFormat.format(new Date()) + ".png"));
System.out.println("Screenshot taken: " + screenName);
Reporter.log("Screenshot taken current URL: " + driver.getCurrentUrl(), true);
} catch (Exception e) {
System.out.println("Exception while taking screenshot: " + e.getMessage());
}
}
}
Phần này kế thừa bài trước đọc properties file á nên là tận dụng cho gọn
Tạo key là "exportCapturePath" với giá trị là đường dẫn thư mục cần lưu hình á
Ví dụ: exportCapturePath=ExportData/Images nghĩa là có 2 thư mục theo tuần tự rồi gắn thêm tên file screenshot vào folder này là xong.
Trước đó tạo sẵn 2 folder hoặc quên thì code nó cũng tự tạo cho nếu folder không tồn tại sẵn
2. Record video khi chạy test case
Vụ record này hay nè. Hay hơn cả screenshot vì thằng này giúp mình khi chạy xong đống Test case của project là mang video để đi demo nộp bàn giao luôn =))
Thằng Record này dùng nó phải có thư viện hỗ trợ riêng chứ trong TestNG không có hỗ trợ hàm sẵn
An chỉ các bạn có 2 thư viện là Monte Media và ATU Test Recorder
2.1 Record Screen với thư viện Monte Media
Tải gói thư viện về trước: MonteScreenRecorder.jar
Import nó vào Build Path của project mình
Nếu Maven project thì add thư viện này:
<!-- https://mvnrepository.com/artifact/com.github.stephenc.monte/monte-screen-recorder -->
<dependency>
<groupId>com.github.stephenc.monte</groupId>
<artifactId>monte-screen-recorder</artifactId>
<version>0.7.7.0</version>
</dependency>
Để sử dụng nó thì chúng ta tạo ra 1 class riêng để xây dựng nó ví dụ tên RecordVideo và sau đó chèn các thư viện sau:
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.monte.media.Format;
import org.monte.media.FormatKeys.MediaType;
import org.monte.media.Registry;
import org.monte.media.math.Rational;
import org.monte.screenrecorder.ScreenRecorder;
import static org.monte.media.AudioFormatKeys.*;
import static org.monte.media.VideoFormatKeys.*;
Cái class đó các các bạn phải kế thừa bằng cách extends cái class xây dựng sẵn trong thư viện Monte là ScreenRecorder
Tiếp đến là xây dựng 2 hàm startRecord và stopRecord như sau:
package AnhTester.Record;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.monte.media.Format;
import org.monte.media.FormatKeys.MediaType;
import org.monte.media.Registry;
import org.monte.media.math.Rational;
import org.monte.screenrecorder.ScreenRecorder;
import static org.monte.media.AudioFormatKeys.*;
import static org.monte.media.VideoFormatKeys.*;
public class RecordVideo extends ScreenRecorder {
// ------Record with Monte Media library---------
public static ScreenRecorder screenRecorder;
public String name;
//Hàm xây dựng
public RecordVideo(GraphicsConfiguration cfg, Rectangle captureArea, Format fileFormat, Format screenFormat,
Format mouseFormat, Format audioFormat, File movieFolder, String name) throws IOException, AWTException {
super(cfg, captureArea, fileFormat, screenFormat, mouseFormat, audioFormat, movieFolder);
this.name = name;
}
//Hàm này bắt buộc để ghi đè custom lại hàm trong thư viên viết sẵn
@Override
protected File createMovieFile(Format fileFormat) throws IOException {
if (!movieFolder.exists()) {
movieFolder.mkdirs();
} else if (!movieFolder.isDirectory()) {
throw new IOException("\"" + movieFolder + "\" is not a directory.");
}
SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH-mm-ss");
return new File(movieFolder,
name + "-" + dateFormat.format(new Date()) + "." + Registry.getInstance().getExtension(fileFormat));
}
// Hàm Start record video
public static void startRecord(String methodName) throws Exception {
//Tạo thư mục để lưu file video vào
File file = new File("./test-recordings/");
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int width = screenSize.width;
int height = screenSize.height;
Rectangle captureSize = new Rectangle(0, 0, width, height);
GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice()
.getDefaultConfiguration();
screenRecorder = new RecordVideo(gc, captureSize,
new Format(MediaTypeKey, MediaType.FILE, MimeTypeKey, MIME_AVI),
new Format(MediaTypeKey, MediaType.VIDEO, EncodingKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE,
CompressorNameKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE, DepthKey, 24, FrameRateKey,
Rational.valueOf(15), QualityKey, 1.0f, KeyFrameIntervalKey, 15 * 60),
new Format(MediaTypeKey, MediaType.VIDEO, EncodingKey, "black", FrameRateKey, Rational.valueOf(30)),
null, file, methodName);
screenRecorder.start();
}
// Stop record video
public static void stopRecord() throws Exception {
screenRecorder.stop();
}
}
Vậy là đã xong 2 hàm rồi
Bây giờ gọi lại 2 hàm đó thì để cái hàm startRecord vào chổ @BeforeClass và stopRecord vào chổ @AfterClass ở từng class test case là xong. Như này nè
package AnhTester.Record;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import io.github.bonigarcia.wdm.WebDriverManager;
public class RecordVideoCallFunction {
private WebDriver driver;
@BeforeClass
public void setupClass() throws Exception {
WebDriverManager.chromedriver().setup();
driver = new ChromeDriver();
driver.manage().timeouts().pageLoadTimeout(30, TimeUnit.SECONDS);
driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);
driver.manage().window().maximize();
// Gọi lại hàm startRecord
RecordVideo.startRecord("ManageDocument");
}
@Test
public void homePage() throws Exception {
driver.get("https://anhtester.com");
Thread.sleep(2000);
driver.findElement(By.id("btn-login")).click();
}
@AfterTest
public void tearDownTestCase() throws Exception {
Thread.sleep(2000);
driver.quit();
}
@AfterClass
public void tearDownClass() throws Exception {
// Gọi lại hàm startRecord
RecordVideo.stopRecord();
}
}
Chạy xong nó xuất ra video như này đây
Thằng Monte này xuất file đuôi là AVI khá sắc nét.
2.2 Record Screen với thư viện ATU Test Recorder
Tải cái gói thư viện jar về: ATUTestRecorder.jar
Bắt đầu dùng thì cũng phải là chèn gói đó vào Build Path của project thôi =))
Cũng tương tự Monte trên thì chúng ta tạo class và chèn thư viện vào
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import atu.testrecorder.ATUTestRecorder;
Dô code luôn nào cho nhanh gọn lẹ
package AnhTester.Record;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import atu.testrecorder.ATUTestRecorder;
public class RecordVideo {
// ------Record with ATU library-----------
public static ATUTestRecorder recorder;
static DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH-mm-ss");
static Date date = new Date();
public static void startRecordATU(String videoName) throws Exception {
recorder = new ATUTestRecorder("./test-recordings/", videoName + "-" + dateFormat.format(date), false);
recorder.start();
}
public static void stopRecordATU() throws Exception {
recorder.stop();
}
}
Nó đơn giản hơn thằng Monte nữa đúng không. Khác là thằng này xuất file đuôi là MOV nhẹ hơn AVI
Và chúng ta gọi 2 hàm startRecordATU và stopRecordATU giống như Monte bên trên là bỏ vào tuần tự @BeforeClass và @AfterClass
package AnhTester.Record;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import io.github.bonigarcia.wdm.WebDriverManager;
public class RecordVideoCallFunction {
private WebDriver driver;
@BeforeClass
public void setupClass() throws Exception {
WebDriverManager.chromedriver().setup();
driver = new ChromeDriver();
driver.manage().timeouts().pageLoadTimeout(30, TimeUnit.SECONDS);
driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);
driver.manage().window().maximize();
// Gọi lại hàm startRecordATU
RecordVideo.startRecordATU("ManageDocument");
}
@Test
public void homePage() throws Exception {
driver.get("https://anhtester.com");
Thread.sleep(2000);
driver.findElement(By.id("btn-login")).click();
}
@AfterTest
public void tearDownTestCase() throws Exception {
Thread.sleep(2000);
driver.quit();
}
@AfterClass
public void tearDownClass() throws Exception {
// Gọi lại hàm startRecordATU
RecordVideo.stopRecordATU();
}
}
Xuất file video đuôi MOV
Và các bạn khi đã nắm từng class rõ rồi của 2 thư viện thì giờ gộp lại chung 1 class luôn cho dễ gọi
package AnhTester.Record;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.monte.media.Format;
import org.monte.media.FormatKeys.MediaType;
import org.monte.media.Registry;
import org.monte.media.math.Rational;
import org.monte.screenrecorder.ScreenRecorder;
import static org.monte.media.AudioFormatKeys.*;
import static org.monte.media.VideoFormatKeys.*;
import atu.testrecorder.ATUTestRecorder;
public class RecordVideo extends ScreenRecorder {
// ------Record with Monte Media library---------
public static ScreenRecorder screenRecorder;
public String name;
//Hàm xây dựng
public RecordVideo(GraphicsConfiguration cfg, Rectangle captureArea, Format fileFormat, Format screenFormat,
Format mouseFormat, Format audioFormat, File movieFolder, String name) throws IOException, AWTException {
super(cfg, captureArea, fileFormat, screenFormat, mouseFormat, audioFormat, movieFolder);
this.name = name;
}
//Hàm này bắt buộc để ghi đè custom lại hàm trong thư viên viết sẵn
@Override
protected File createMovieFile(Format fileFormat) throws IOException {
if (!movieFolder.exists()) {
movieFolder.mkdirs();
} else if (!movieFolder.isDirectory()) {
throw new IOException("\"" + movieFolder + "\" is not a directory.");
}
SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH-mm-ss");
return new File(movieFolder,
name + "-" + dateFormat.format(new Date()) + "." + Registry.getInstance().getExtension(fileFormat));
}
// Hàm Start record video
public static void startRecord(String methodName) throws Exception {
//Tạo thư mục để lưu file video vào
File file = new File("./test-recordings/");
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int width = screenSize.width;
int height = screenSize.height;
Rectangle captureSize = new Rectangle(0, 0, width, height);
GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice()
.getDefaultConfiguration();
screenRecorder = new RecordVideo(gc, captureSize,
new Format(MediaTypeKey, MediaType.FILE, MimeTypeKey, MIME_AVI),
new Format(MediaTypeKey, MediaType.VIDEO, EncodingKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE,
CompressorNameKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE, DepthKey, 24, FrameRateKey,
Rational.valueOf(15), QualityKey, 1.0f, KeyFrameIntervalKey, 15 * 60),
new Format(MediaTypeKey, MediaType.VIDEO, EncodingKey, "black", FrameRateKey, Rational.valueOf(30)),
null, file, methodName);
screenRecorder.start();
}
// Stop record video
public static void stopRecord() throws Exception {
screenRecorder.stop();
}
// ------Record with ATU library-----------
public static ATUTestRecorder recorder;
static DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH-mm-ss");
static Date date = new Date();
public static void startRecordATU(String videoName) throws Exception {
recorder = new ATUTestRecorder("./test-recordings/", videoName + "-" + dateFormat.format(date), false);
recorder.start();
}
public static void stopRecordATU() throws Exception {
recorder.stop();
}
}
Khi qua Test Case thì dùng thằng nào gọi cặp start và stop tương ứng thôi.
Vậy là xong rồi.
Anh Tester
facebook.com/anhtester
Đường dẫu khó chân vẫn cần bước đi
Đời dẫu khổ tâm vẫn cần nghĩ thấu