การทดสอบหน่วยด้วย testthat
เขียนบล็อก test_that() ใช้ข้อคาดหมาย และเรียกใช้ tests ด้วย devtools::test()
การทดสอบหน่วยด้วย testthat เป็นบทเรียน R Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน R Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส R Academy มีบทเรียนทั้งหมด 4 บทเรียน
เหตุใดจึงต้องทดสอบหน่วย
การทดสอบหน่วยจะตรวจสอบโดยอัตโนมัติว่าฟังก์ชันแต่ละรายการทำงานได้ถูกต้องหรือไม่ การทดสอบช่วยตรวจจับการถดถอยเมื่อคุณเปลี่ยนโค้ด ทำหน้าที่เป็นเอกสารประกอบที่เรียกใช้ได้ และช่วยให้คุณปรับโครงสร้างโค้ดใหม่ได้อย่างมั่นใจและปลอดภัย แพ็กเกจ testthat เป็นกรอบงานมาตรฐานสำหรับการทดสอบแพ็กเกจ R
การตั้งค่า testthat
usethis::use_testthat() จะเพิ่ม testthat ลงใน Suggests ใน DESCRIPTION สร้าง tests/testthat/ และสร้างสคริปต์ตัวเรียกใช้ tests/testthat.R ให้เรียกใช้คำสั่งนี้หนึ่งครั้งเมื่อเริ่มต้นแพ็กเกจใหม่
# usethis::use_testthat()
#
# Creates:
# tests/
# testthat.R <- runner (do not edit)
# testthat/
# (empty — write test files here)
#
# Updates DESCRIPTION:
# Suggests: testthat (>= 3.0.0)
# Config/testthat/edition: 3การสร้างไฟล์การทดสอบ
usethis::use_test('add') จะสร้าง tests/testthat/test-add.R ตามธรรมเนียม ไฟล์การทดสอบจะตั้งชื่อเป็น test-{function_name}.R แต่ละไฟล์จะรวมการทดสอบสำหรับฟังก์ชันหรือความสามารถหนึ่งรายการ
# usethis::use_test('add') # creates tests/testthat/test-add.R
#
# Content of test-add.R:
# test_that('add() returns correct sum', {
# expect_equal(add(1, 2), 3)
# expect_equal(add(-1, 1), 0)
# expect_equal(add(0.1, 0.2), 0.3, tolerance = 1e-7)
# })โครงสร้าง test_that()
test_that('description', { ... }) ใช้จัดกลุ่มสิ่งที่คาดหวังที่เกี่ยวข้องกัน สตริงคำอธิบายควรเติมประโยค 'test that ...' ให้สมบูรณ์ และต้องเฉพาะเจาะจงมากพอที่จะเป็นประโยชน์ในข้อความแจ้งเมื่อการทดสอบล้มเหลว
# Good test_that descriptions:
# test_that('add() handles negative numbers', { ... })
# test_that('add() recycles length-1 vectors', { ... })
# test_that('add() returns NA when input contains NA', { ... })
#
# Bad (too vague):
# test_that('it works', { ... })
# test_that('test1', { ... })expect_equal() และ expect_identical()
expect_equal(actual, expected) ทดสอบโดยยอมให้มีค่าคลาดเคลื่อนทางตัวเลขสำหรับเลขทศนิยม expect_identical(actual, expected) ต้องการความเท่ากันทุกประการ รวมถึงชนิดข้อมูลด้วย ในกรณีส่วนใหญ่ ควรใช้ expect_equal() มากกว่า
# test_that('add() adds correctly', {
# expect_equal(add(1, 2), 3) # numeric equality
# expect_equal(add(0.1, 0.2), 0.3) # tolerance handles floating point
# expect_identical(add(1L, 2L), 3L) # exact type match: integer
# expect_identical(add(1.0, 2.0), 3.0) # exact type match: double
# })expect_error() และ expect_warning()
ทดสอบว่าฟังก์ชันสร้างข้อผิดพลาดและคำเตือนที่ถูกต้องหรือไม่ ส่งรูปแบบ regex เพื่อจับคู่กับข้อความข้อผิดพลาด — วิธีนี้ช่วยยืนยันว่าเป็นข้อผิดพลาดที่ถูกต้อง ไม่ใช่ข้อผิดพลาดใด ๆ ก็ได้
# test_that('add() validates input types', {
# expect_error(
# add('a', 2),
# regexp = 'numeric' # message must contain 'numeric'
# )
# expect_error(
# add(NULL, 1),
# regexp = 'numeric'
# )
# })
#
# test_that('sqrt() warns on negative input', {
# expect_warning(sqrt(-1))
# })expect_true() และ expect_false()
expect_true(expr) และ expect_false(expr) ใช้ทดสอบเงื่อนไขตรรกะ ใช้ฟังก์ชันเหล่านี้เมื่อทดสอบเพรดิเคตหรือเงื่อนไขที่ส่งกลับค่าตรรกะเดียว
# test_that('is_positive() returns correct logical', {
# expect_true(is_positive(5))
# expect_true(is_positive(0.001))
# expect_false(is_positive(0))
# expect_false(is_positive(-3))
# })
#
# # Also useful for vector tests:
# test_that('add() result has correct length', {
# result <- add(c(1,2,3), c(4,5,6))
# expect_true(length(result) == 3)
# })ฟังก์ชันตรวจสอบสิ่งที่คาดหวังเพิ่มเติม
testthat มีฟังก์ชันตรวจสอบสิ่งที่คาดหวังมากมายสำหรับสถานการณ์ต่าง ๆ:
expect_length(x, n)— ตรวจสอบความยาวเวกเตอร์expect_type(x, 'double')— ตรวจสอบชนิดพื้นฐานexpect_s3_class(x, 'data.frame')— ตรวจสอบคลาส S3expect_null(x)— ตรวจสอบว่าเป็น NULLexpect_match(string, regexp)— ตรวจสอบรูปแบบสตริง
# test_that('add() output has correct type and length', {
# result <- add(c(1.0, 2.0), c(3.0, 4.0))
# expect_type(result, 'double')
# expect_length(result, 2)
# })
#
# test_that('summary_stats() returns a data frame', {
# result <- summary_stats(rnorm(100))
# expect_s3_class(result, 'data.frame')
# })การเรียกใช้การทดสอบด้วย devtools::test()
devtools::test() (Ctrl+Shift+T) เรียกใช้ไฟล์การทดสอบทั้งหมดและแสดงสรุปการทดสอบที่ผ่าน ล้มเหลว และคำเตือน การทดสอบแต่ละรายการที่ล้มเหลวจะแสดงสิ่งที่คาดหวังที่ไม่ผ่าน รวมถึงค่าจริงเทียบกับค่าที่คาดหวัง
# devtools::test()
#
# Example output:
# == Testing mypackage ====================================
# v | OK F W S | Context
# v | 3 | add [0.1s]
# v | 4 | subtract [0.1s]
# x | 2 1 | multiply [0.2s]
# -- Failure (test-multiply.R:5): multiply() handles zero
# multiply(5, 0) not equal to 0.
# Actual: 5
# Expected: 0
# ==========================================================
# [ FAIL 1 | WARN 0 | SKIP 0 | PASS 9 ]ความครอบคลุมการทดสอบด้วย covr
covr::package_coverage() วัดเปอร์เซ็นต์ของบรรทัดในแพ็กเกจที่การทดสอบเรียกใช้ covr::report() จะเปิดรายงาน HTML ที่แสดงบรรทัดที่ครอบคลุม (สีเขียว) และไม่ครอบคลุม (สีแดง) ควรตั้งเป้าให้มีความครอบคลุมอย่างน้อย 80%
# library(covr)
# cov <- package_coverage()
# print(cov)
#
# Example output:
# mypackage Coverage: 87.50%
# R/add.R: 100.00%
# R/subtract.R: 100.00%
# R/utils.R: 62.50% <- needs more tests!
#
# covr::report() # interactive HTML report
# covr::zero_coverage(cov) # list uncovered linesการทดสอบกรณีขอบเขต
การทดสอบที่ดีไม่ควรครอบคลุมเฉพาะเส้นทางปกติที่ทุกอย่างเป็นไปด้วยดี แต่ควรครอบคลุมกรณีขอบเขตด้วย:
- อินพุตว่าง:
numeric(0),character(0) - อินพุต NA: ฟังก์ชันส่งต่อหรือจัดการ NA อย่างไร
- อินพุตที่มีความยาว 1 เทียบกับความยาว n
- ค่าขอบเขต: 0 จำนวนติดลบ ค่าที่มีขนาดใหญ่มาก
- ชนิดข้อมูลไม่ถูกต้อง: จะเกิดอะไรขึ้นเมื่อผู้ใช้ส่งสตริงให้ฟังก์ชันที่รับค่าตัวเลข
# test_that('add() handles edge cases', {
# expect_equal(add(numeric(0), numeric(0)), numeric(0)) # empty
# expect_true(is.na(add(NA, 1))) # NA propagation
# expect_equal(add(1, c(1,2,3)), c(2,3,4)) # recycling
# expect_equal(add(.Machine$integer.max, 0L), # boundary
# .Machine$integer.max)
# })ตรวจสอบอย่างรวดเร็ว: expect_error()
คุณเรียกใช้ expect_error(my_fn('bad'), regexp = 'invalid input') การทดสอบนี้ตรวจสอบอะไร
ทบทวนการทดสอบหน่วย
การทดสอบแพ็กเกจ R ด้วย testthat:
usethis::use_testthat()— ตั้งค่าโครงสร้างพื้นฐานสำหรับการทดสอบเพียงครั้งเดียวusethis::use_test('fn')— สร้างtests/testthat/test-fn.Rtest_that('description', {...})— จัดกลุ่มข้อคาดหมายที่เกี่ยวข้องกันexpect_equal(),expect_error(),expect_warning(),expect_true(),expect_false()— ฟังก์ชันข้อคาดหมายหลักdevtools::test()— เรียกใช้การทดสอบทั้งหมด (Ctrl+Shift+T)covr::package_coverage()— วัดความครอบคลุมของการทดสอบ
คำถามที่พบบ่อย
บทเรียน “การทดสอบหน่วยด้วย testthat” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การทดสอบหน่วยด้วย testthat” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส R Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส R Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การทดสอบหน่วยด้วย testthat”
เขียนบล็อก test_that() ใช้ข้อคาดหมาย และเรียกใช้ tests ด้วย devtools::test() คุณปฏิบัติ R Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน R Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน R Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน
บทเรียน “การทดสอบหน่วยด้วย testthat” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน R Academy นี้ได้ไหม
ได้ บทเรียน R Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- โครงสร้างแพ็กเกจด้วย usethis และ devtools
- การจัดทำเอกสารฟังก์ชันด้วย roxygen2
- การทดสอบหน่วยด้วย testthat
- การส่งแพ็กเกจไปยัง CRAN และการบำรุงรักษา