如何将XCTestCases暴露给外部测试包? [英] How to expose XCTestCases to external test bundles?

查看:115
本文介绍了如何将XCTestCases暴露给外部测试包?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个框架Whiteboard,它封装了我的业务逻辑.我试图保持依赖关系的反向和解耦,因此由于Whiteboard依赖于存储库,因此它声明了协议WhiteboardRepository,并且它希望链接到Whiteboard的客户端提供WhiteboardRepository的实现./p>

您可以在下面的屏幕截图中看到它.还要注意WhiteboardTests组,它包括一个WhiteboardRepositoryTests类以及一个WhiteboardRepositoryFake和它自己的测试子类.

为确保WhiteboardRepository的实现按预期方式运行,WhiteboardTests测试包定义了XCTestCaseWhiteboardRepositoryTests子类:

class WhiteboardRepositoryTests: XCTestCase {
  var repo: WhiteboardRepository?

  override func setUp() {
    if let repo = repo {
      // test setup here
    }
  }

  // test cases here

}

为了使Whiteboard的客户端测试其WhiteboardRepository的实现,该实现的测试类将WhiteboardRepositoryTests子类化,并将实现的一个实例提供给测试子类,然后该子类在以下情况下使用该实例:运行测试.

例如,这是WhiteboardRepositoryFakeTests的样子:

class WhiteboardRepositoryFakeTests: WhiteboardRepositoryTests {
    override func setUp() {
        repo = WhiteboardRepositoryFake()
        super.setUp()
    }

    // the test classes run, using the instance of WhiteboardRepositoryFake()
}

这当然很好,因为WhiteboardRepositoryFakeTestsWhiteboardTests捆绑包中,所以WhiteboardRepositoryTests暴露于WhiteboardRepositoryFakeTests.

问题是:链接到Whiteboard的应用将需要创建自己的WhiteboardRepositoryTests子类来测试自己的实现,但是由于它们无法访问WhiteboardTests测试包,因此它们不了解WhiteboardRepositoryTests类,因此无法对其进行子类化.

我有多个使用Whiteboard的客户端,所以我不能简单地将WhiteboardRepositoryTests类复制到每个客户端中-我也不想这样做,因为Whiteboard的职责是定义WhiteboardRepository的行为,因此WhiteboardRepositoryTests应该放在Whiteboard的测试包中.在理想的世界中,我可以将Whiteboard的测试包链接或注入到客户的测试包中,以便WhiteboardRepositoryTests可以接受客户的测试,但是我看不到如何做到这一点.

有没有办法解决这个障碍?如何将WhiteboardRepositoryTests暴露给客户端测试包中的测试,以便客户端可以确保其WhiteboardRepository的实现行为符合预期?

解决方案

此处的主要问题是WhiteboardTests目标可能是测试目标,并且实际上并未构建可链接框架.这使我们无法建立另一个目标(您的客户测试),并从该目标中导入和子类化.例如,您会注意到测试目标缺少用于从框架中导出符号的常规{TARGET_NAME} .h头文件.我们需要的是构建框架但与XCTest链接的目标.客户项目的测试目标将与此框架链接,以导入和提供其提供的XCTestCase类.那么该怎么做:

  1. 在Whiteboard项目中创建框架构建目标,方法是单击项目文件,然后单击编辑器">添加目标".现在将其称为WhiteboardAbstractTests.该目标将与XCTest链接并构建一个框架,以提供您希望客户项目的测试成为子类的超类.

  2. 现在,与XCTest链接有点奇怪,因为它们不再使它在Xcode中可用.但是,您仍然可以在Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Frameworks/XCTest.framework处找到XCTest框架,并将其拖到Xcode的WhiteboardAbstractTests目标文件夹中.

  3. 从这里,您应该能够在WhiteboardAbstractTests中定义XCTestCase子类,该子类将内置在WhiteboardAbstractTests.framework中.

  4. 在您的客户项目中,我假设Whiteboard xcodeproj是通过Cocoapods,Carthage或git子模块以某种方式嵌套的.找到Whiteboard xcodeproj文件,并将其拖到Xcode中的客户端项目测试目标中.

  5. 在Xcode中选择客户端项目文件,然后转到构建阶段".在目标依赖项"下,单击加号按钮,您应该会从Whiteboard中看到各种构建目标.选择"WhiteboardAbstractTests".这样可以确保Xcode在构建客户端测试之前先构建抽象测试.

  6. 也在构建阶段的与库链接二进制"下,选择"WhiteboardAbstractTests",以确保我们的测试目标链接并可以从Whiteboard提供的抽象测试中导入类.

从这里开始,您应该能够在将建立WhiteboardAbstractTests的客户端项目中运行测试,将客户端测试与之链接,并运行将抽象测试作为子类的客户端测试.我已经对其进行了快速测试,但是如果您遇到麻烦,请告诉我.

P.S.我喜欢你的建筑风格.马丁·福勒(Martin Fowler)在某个地方流下了喜悦之泪.

I have a framework Whiteboard that encapsulates my business logic. I'm trying to keep my dependencies inverted and decoupled, so since Whiteboard depends on a repository, it declares a protocol WhiteboardRepository, and it expects clients that link against Whiteboard to supply an implementation of WhiteboardRepository.

You can see this in the screen shot below. Note also the WhiteboardTests group, which includes a WhiteboardRepositoryTests class along with a WhiteboardRepositoryFake and its own test subclass.

To ensure that implementations of WhiteboardRepository behave as expected, the WhiteboardTests test bundle defines a WhiteboardRepositoryTests subclass of XCTestCase:

class WhiteboardRepositoryTests: XCTestCase {
  var repo: WhiteboardRepository?

  override func setUp() {
    if let repo = repo {
      // test setup here
    }
  }

  // test cases here

}

In order for a client of Whiteboard to test its implementation of WhiteboardRepository, the test class for the implementation subclasses the WhiteboardRepositoryTests and supplies an instance of the implementation to the test subclass, which then uses that instance when running the tests.

For example, here's what WhiteboardRepositoryFakeTests looks like:

class WhiteboardRepositoryFakeTests: WhiteboardRepositoryTests {
    override func setUp() {
        repo = WhiteboardRepositoryFake()
        super.setUp()
    }

    // the test classes run, using the instance of WhiteboardRepositoryFake()
}

This works fine, of course, since WhiteboardRepositoryFakeTests is in the WhiteboardTests bundle, so WhiteboardRepositoryTests is exposed to WhiteboardRepositoryFakeTests.

The problem is: apps that link against Whiteboard will need to create their own subclass of WhiteboardRepositoryTests to test their own implementation, but because they don't have access to the WhiteboardTests test bundle, they're not aware of the WhiteboardRepositoryTests class and so can't subclass it.

I've got multiple clients consuming Whiteboard, so I can't simply copy the WhiteboardRepositoryTests class into each client—and nor would I want to, since it's the responsibility of Whiteboard to define WhiteboardRepository's behavior, so WhiteboardRepositoryTests should live in Whiteboard's test bundle. In an ideal world, I'd be able to link or inject Whiteboard's test bundle into a client's test bundle so that WhiteboardRepositoryTests is exposed to the client's tests, but I don't see how I can do this.

Is there any way around this obstacle? How can I expose WhiteboardRepositoryTests to tests within a client test bundle so that the client can make sure its implementation of WhiteboardRepository behaves as expected?

解决方案

The main problem here is that the WhiteboardTests target is likely a test target and does not actually build a linkable framework. This prevents us from building another target (your client tests) that imports and subclass from it. For example, you'll notice that a test target lacks the normal {TARGET_NAME}.h header file that is used to export symbols from the framework. What we need is a target that builds a framework but links with XCTest. Client projects' test targets will link with this framework to import and subclass the XCTestCase classes it provides. So what to do:

  1. Create a framework building target in your Whiteboard project by clicking the project file then Editor > Add Target. Let's call this WhiteboardAbstractTests for now. This target will link with XCTest and build a framework to supply the superclasses that you want your client projects' tests to subclass.

  2. Now, to link with XCTest is a bit weird because they don't make it readily available in Xcode anymore. However, you can still find the XCTest framework at Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Frameworks/XCTest.framework and drag this into the WhiteboardAbstractTests target folder in Xcode.

  3. From here, you should be able to define XCTestCase subclasses in WhiteboardAbstractTests that will be built into the WhiteboardAbstractTests.framework.

  4. In your client project, I assume that the Whiteboard xcodeproj is nested in some way, either by Cocoapods, Carthage, or git submodule. Find the Whiteboard xcodeproj file and drag it into the client project test target in Xcode.

  5. Select the client project file in Xcode and go to Build Phases. Under Target dependencies, click the plus button and you should see various build targets from Whiteboard. Select WhiteboardAbstractTests. This will make sure Xcode builds the abstract tests before building your clients tests.

  6. Also in Build Phases, under Link Binary With Libraries, select WhiteboardAbstractTests to make sure that our test target links with and can import classes from the abstract tests provided by Whiteboard.

From here you should be able to run the tests in your client project which will build WhiteboardAbstractTests, link your client tests with it, and run your client tests which subclass the abstract tests. I've quickly tested it out but let me know if you have trouble.

P.S. I like your architectural style. Somewhere Martin Fowler sheds a tear of joy.

这篇关于如何将XCTestCases暴露给外部测试包?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆