#ios #objective-c #swift #testing #swift-package-manager
#iOS #objective-c #swift #тестирование #swift-package-manager
Вопрос:
Я переношу устаревшую Objective-C
библиотеку в пакет Swift, в котором написаны модульные тесты Swift
. Ранее эта библиотека была написана с использованием стандартного подхода: Framework target / Xcode project, поэтому цель тестирования могла импортировать внутренние заголовки (используя заголовок bridging). Похоже, что Swift Package Manager не предлагает ничего подобного, поэтому тестирование внутренних компонентов выглядит проблематичным (без их публичного раскрытия).
Кстати: @testable import ...
не работает для Objective-C
кода.
Я также пытался обмануть его, выставив их с помощью:
cSettings: [.headerSearchPath("../ObjcPackage/Source/Internal")]
Но это не работает.
Я использую swift-tools-version:5.3
Репозиторий, демонстрирующий проблему:
https://github.com/lukaszmargielewski/ObjcPackage
Некоторые фрагменты:
1. Определение пакета:
// swift-tools-version:5.3
// Package.swift
import PackageDescription
let package = Package(
name: "ObjcPackage",
platforms: [
.iOS(.v10)
],
products: [
.library(name: "ObjcPackage", targets: ["ObjcPackage"]),
],
targets: [
.target(
name: "ObjcPackage",
path: "ObjcPackage/Source",
publicHeadersPath: "Public",
cSettings: [
.headerSearchPath("Public"),
.headerSearchPath("Internal"),
]
),
.testTarget(
name: "ObjcPackageTests",
dependencies: ["ObjcPackage"],
path: "ObjcPackageTests",
sources: ["Source"],
cSettings: [
.headerSearchPath("../ObjcPackage/Source/Internal")
]
)
]
)
2. Общедоступный заголовок — ObjcPackage
// ObjcPackage.h
#import <Foundation/Foundation.h>
@interface ObjcPackage : NSObject
- (nullable instancetype)initWithTitle:(nonnull NSString *)title NS_DESIGNATED_INITIALIZER;
- (nonnull instancetype)init NS_UNAVAILABLE;
@property (nonnull, readonly, nonatomic, copy) NSString *title;
@end
3. Внутренний заголовок — ObjcPackage
// ObjcPackage Internal.h
#import <Foundation/Foundation.h>
#import "ObjcPackage.h"
/// Internal methods of ObjcPackage
@interface ObjcPackage ()
- (NSString *)generateInternalSecret;
@end
4. Файл реализации — ObjcPackage
// ObjcPackage.m
#import "ObjcPackage.h"
#import "ObjcPackage Internal.h"
@interface ObjcPackage()
@property (nonatomic, readwrite, copy) NSString *title;
@end
@implementation ObjcPackage
- (instancetype)initWithTitle:(NSString *)title {
self = [super init];
if (self != nil) {
self.title = title;
}
return self;
}
- (NSString *)generateInternalSecret {
return [NSString stringWithFormat:@"%@_internal_secret", self.title];
}
@end
5. Тестовый пример (ObjcPackageTests)
// ObjcPackageTests.swift
import Foundation
import XCTest
@testable import ObjcPackage
class ObjcPackageTests: XCTestCase {
private lazy var objcPackage = ObjcPackage(title: "A")
func testPublic() {
XCTAssertEqual(objcPackage?.title, "A")
}
func testInternal() {
// How to expose internal Objective-C header to the test package,
// without making it public?
// !!! - COMPILATION ERROR: - !!!
XCTAssertEqual(objcPackage.generateInternalSecret, "A_internal_secret")
}
}
Комментарии:
1. Насколько я помню, objc на самом деле не имеет частного механизма. (Неоптимальным) решением было бы в любом случае поместить
generateInternalSecret
общедоступный заголовок и использовать_
префикс, чтобы указать, что это не должно вызываться вне пакета.