Swift 包资源颜色在 CI 测试用例中返回为零。但是在本地测试用例中返回正常

Posted

技术标签:

【中文标题】Swift 包资源颜色在 CI 测试用例中返回为零。但是在本地测试用例中返回正常【英文标题】:Swift Package resource colors coming back as nil in CI test cases. But return fine during local test cases 【发布时间】:2020-12-11 04:25:44 【问题描述】:

奇怪的是,这只发生在 CI (GitHub Actions) 上运行时。

这是我的资产目录:

这是我对 UIColor 的扩展以访问这些颜色:

// MARK: - UIKit

public extension UIColor 
    // MARK: Design System Colors

    static let primaryColor = UIColor(named: "PrimaryColor", in: .module, compatibleWith: nil)!
    static let primaryVariant = UIColor(named: "PrimaryVariant", in: .module, compatibleWith: nil)!
    static let onPrimary = UIColor(named: "OnPrimary", in: .module, compatibleWith: nil)!
    static let onPrimaryMedium = UIColor(named: "OnPrimaryMedium", in: .module, compatibleWith: nil)!
    static let onPrimaryDisabled = UIColor(named: "OnPrimaryDisabled", in: .module, compatibleWith: nil)!

    static let surface = UIColor(named: "Surface", in: .module, compatibleWith: nil)!
    static let onSurface = UIColor(named: "OnSurface", in: .module, compatibleWith: nil)!
    static let onSurfaceMedium = UIColor(named: "OnSurfaceMedium", in: .module, compatibleWith: nil)!
    static let onSurfaceDisabled = UIColor(named: "OnSurfaceDisabled", in: .module, compatibleWith: nil)!

    static let background = UIColor(named: "Background", in: .module, compatibleWith: nil)!
    static let onBackground = UIColor(named: "OnBackground", in: .module, compatibleWith: nil)!

    static let accent = UIColor(named: "Accent", in: .module, compatibleWith: nil)!
    static let divider = UIColor(named: "Divider", in: .module, compatibleWith: nil)!

    // MARK: Status Colors

    static let statusSuccess = UIColor(named: "StatusSuccess", in: .module, compatibleWith: nil)!
    static let statusWarning = UIColor(named: "StatusWarning", in: .module, compatibleWith: nil)!
    static let statusError = UIColor(named: "StatusError", in: .module, compatibleWith: nil)!

    // MARK: Map Colors

    static let pinPlace = UIColor(named: "PinPlace", in: .module, compatibleWith: nil)!
    static let pinRoute = UIColor(named: "PinRoute", in: .module, compatibleWith: nil)!
    static let polyline = UIColor(named: "Polyline", in: .module, compatibleWith: nil)!
    static let polylineRoute = UIColor(named: "PolylineRoute", in: .module, compatibleWith: nil)!

我有一个测试用例,它只遍历所有这些颜色并检查它们是否可以从模块包中加载(以便强制解包它们是安全的):

@testable import MyProject
import SwiftUI
import UIKit
import XCTest

/// Test to ensure that the colors can be loaded from the module bundle without crashing.
class MyProjectColorTest: XCTestCase 
    func testLoadUIColorFromBundle() 
        for color in UIColor.myProjectUIColors 
            XCTAssertNotNil(color)
        
    


fileprivate extension UIColor 
    static var myProjectUIColors: [UIColor] = [
        UIColor.primaryColor,
        UIColor.primaryVariant, //It fails when it tries to access this color.
        UIColor.onPrimary,
        UIColor.onPrimaryMedium,
        UIColor.onPrimaryDisabled,
        UIColor.surface,
        UIColor.onSurface,
        UIColor.onSurfaceMedium,
        UIColor.onSurfaceDisabled,
        UIColor.background,
        UIColor.onBackground,
        UIColor.accent,
        UIColor.divider,
        UIColor.statusSuccess,
        UIColor.statusWarning,
        UIColor.statusError,
        UIColor.pinPlace,
        UIColor.pinRoute,
        UIColor.polyline,
        UIColor.polylineRoute,
    ]

最后:

这是我的包清单:

// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "MyProject",
    defaultLocalization: "en",
    platforms: [
        .ios(.v13),
        .watchOS(.v6),
    ],
    products: [
        // Products define the executables and libraries a package produces, and make them visible to other packages.
        .library(
            name: "MyProject",
            targets: ["MyProject"]
        ),
    ],
    dependencies: [
        // Dependencies declare other packages that this package depends on.
        // .package(url: /* package url */, from: "1.0.0"),
    ],
    targets: [
        // Targets are the basic building blocks of a package. A target can define a module or a test suite.
        // Targets can depend on other targets in this package, and on products in packages this package depends on.
        .target(
            name: "MyProject",
            dependencies: [],
            exclude: [
                "MyProjectDemo/*",
            ],
            // I've also tried omitting this and letting SPM handle it automatically.
            resources: [
                .process("*.xcassets"),
            ]
        ),
        .testTarget(
            name: "MyProjectTests",
            dependencies: ["MyProject"]
        ),
    ]
)

另外,这是我的 GitHub 操作脚本:

name: Build

on:
  push:
    branches: [ master ]
    paths:
       - '.github/workflows/swiftlint.yml'
       - '.swiftlint.yml'
       - '**/*.swift'
  pull_request:
    branches: [ master, develop ]
    paths:
       - '.github/workflows/swiftlint.yml'
       - '.swiftlint.yml'
       - '**/*.swift'

jobs:
  Build:
    runs-on: macos-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2.3.4
      - name: List available Xcode versions
        run: ls /Applications | grep Xcode
      - name: Select Xcode
        run: sudo xcode-select -switch /Applications/Xcode_12.2.app
      - name: Show Build Destinations
        run: xcodebuild -showdestinations -scheme MyProject
      - name: Build
        run: xcodebuild test -scheme MyProject -destination 'platform=iOS Simulator,OS=14.2,name=iPhone 12'
  Lint:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2.3.4
      - name: Lint
        uses: norio-nomura/action-swiftlint@3.2.0

【问题讨论】:

【参考方案1】:

有一个解决方法。在这里成立 -> https://developer.apple.com/forums/thread/664295

import Foundation

class CurrentBundleFinder 

extension Foundation.Bundle 
        
    static var myModule: Bundle = 
        /* The name of your local package, prepended by "LocalPackages_" */
        let bundleName = "UI_UI"
        let candidates = [
            /* Bundle should be present here when the package is linked into an App. */
            Bundle.main.resourceURL,
            /* Bundle should be present here when the package is linked into a framework. */
            Bundle(for: CurrentBundleFinder.self).resourceURL,
            /* For command-line tools. */
            Bundle.main.bundleURL,
            /* Bundle should be present here when running previews from a different package (this is the path to "…/Debug-iphonesimulator/"). */
            Bundle(for: CurrentBundleFinder.self).resourceURL?.deletingLastPathComponent().deletingLastPathComponent(),
        ]
        for candidate in candidates 
            let bundlePath = candidate?.appendingPathComponent(bundleName + ".bundle")
            if let bundle = bundlePath.flatMap(Bundle.init(url:)) 
                return bundle
            
        
        fatalError("unable to find bundle named \(bundleName)")
    ()

【讨论】:

以上是关于Swift 包资源颜色在 CI 测试用例中返回为零。但是在本地测试用例中返回正常的主要内容,如果未能解决你的问题,请参考以下文章

从 JUnit 测试用例中找不到资源文件

Travis CI - 无法查询测试包中的测试用例列表:找不到测试包

plpgsql 测试用例中的 SELECT 查询未返回数组

测试用例中的分段错误

单元测试用例 - Swift 3.2

如何在go中模拟测试用例中结构的方法调用