آزمایش اعضای خصوصی با واردات خصوصی
نوشتن تستهای واحد میتواند یک کار چالش برانگیز باشد، زمانی که شی مورد آزمایش دارای ویژگیها یا روشهای خصوصی باشد. در این سناریو استفاده از mock ها قابل اجرا نیست و یک راه حل تکرار شونده نوشتن ویژگی ها یا روش های اضافی برای اعتبارسنجی اعضای خصوصی است که شی را آلوده می کند. در این مقاله من یک راه حل جایگزین ارائه می کنم که شامل استفاده از پرچم های کامپایل و ویژگی های خصوصی زبان برای دسترسی به اعضای خصوصی فقط در محیط تست است.
تنظیمات اولیه
اولین قدم اضافه کردن پرچم است -enable-private-imports
به بسته یا برنامه شما. این پرچم به کامپایلر دستور میدهد تا اعضای داخلی و خصوصی را تا زمانی که در کد منبع مشخص شدهاند در معرض نمایش بگذارد (در ادامه در مورد آن بیشتر توضیح خواهیم داد). در فایل بسته خود پارامتر را اضافه کنید swiftSettings
به توصیف هدف و استفاده از روش unsafeFlags(_:_:)
برای درج پرچم مذکور.
.target(
name: "MyAwesomeLibrary",
dependencies: [
...
],
swiftSettings: [
.unsafeFlags(["-enable-private-imports"], .when(configuration: .debug))
])
در این مثال ما پرچم را به تنظیمات محدود می کنیم اشکال زدایی چون نمی خواهیم کد تولید ما اعضای خصوصی را افشا کند. فقط از یک شی از نوع BuildSettingCondition برای تعیین شرایطی که تحت آن پرچم های کامپایل اعمال می شود استفاده کنید.
اگر می خواهید اعضای خصوصی برنامه خود را در معرض دید قرار دهید، پرچم را در گزینه اضافه کنید سایر پرچم های سوئیفت ما کامپایلر سوئیفت – پرچم های سفارشی و شکم تنظیمات ساخت از پروژه
پیاده سازی
فرض کنید بسته ما دارای کد زیر در فایل Operations.swift است:
public struct Operations {
private var count: Int = 0
public func sum(_ x: Int, _ y: Int) -> Int {
count += 1
return x + y
}
}
متغیر count
تعداد عملیات مجموع انجام شده را ثبت می کند – یعنی تعداد دفعات روش sum(_:_:)
مورد استناد قرار گرفت. بیایید اکنون تست های واحد را برای شیء خود بنویسیم.
import XCTest
@testable import MyAwesomeLibrary
final class OperationsTests: XCTestCase {
private var operations: Operations!
override func setup() {
super.setup()
operations = .init()
}
func testSum() {
let result = operations.sum(2, 3)
XCTAssertEqual(result, 5)
}
}
روش testSum()
فقط بررسی می کند که عملیات جمع شی ما درست است. به منظور آزمایش اینکه آیا متغیر count
با هر تماسی که باید آن را در معرض کلاس آزمایشی خود قرار دهیم، افزایش می یابد. برای این کار ویژگی خصوصی را اضافه می کنیم @_private(sourceFile:)
به دستور واردات ما، نام فایل حاوی کد منبع اعضای خصوصی را که قرار است در معرض نمایش قرار گیرند، به عنوان آرگومان ارسال می کند.
import XCTest
@_private(sourceFile: "Operations.swift")
@testable import MyAwesomeLibrary
final class OperationsTests: XCTestCase {
private var operations: Operations!
override func setup() {
super.setup()
operations = .init()
}
func testSum() {
let result = operations.sum(2, 3)
XCTAssertEqual(result, 5)
}
func testCount() {
let _ = operations.sum(2, 3)
let _ = operations.sum(4, 5)
XCTAssertEqual(operations.count, 2)
}
}
روش testCount()
بررسی می کند که آیا پس از دو تماس از sum(_:_:)
متغیر count
دو برابر افزایش می یابد. ادعا به ملک دسترسی دارد count
از شی operations
بدون ایجاد خطای کامپایل به لطف ویژگی @_private(sourceFile: "Operations.swift")
که در هنگام وارد کردن ماژول وارد کردیم.
به این ترتیب، هر زمان که نیاز به آزمایش اعضای خصوصی یک شی معین داشتید، ماژول را با ویژگی وارد کنید @_private(sourceFile:)
قبل و نام فایل حاوی این شی را به عنوان آرگومان ارسال کنید.
نتیجه
افشای اعضای خصوصی میتواند تستهای واحد نوشتن را با حذف نیاز به اعضای کمکی که کاری جز تماشای سایر اعضا انجام نمیدهند، آسانتر کند. فقط مراقب باشید که پرچم در کامپایل اعمال نشود رهایی به منظور جلوگیری از قرار گرفتن در معرض غیر ضروری اعضای خصوصی.