在测试中使用枚举时避免耦合

时间:2017-07-20 09:08:29

标签: swift unit-testing enums

说我们有这个枚举

data-parsley-min="1"

此枚举以这种方式使用:

enum Action: String {
    case doThing
    case doOtherThing
}

现在,我对func run(action: Action, block: () -> Void) 方法进行单元测试,因此我需要以这种方式传递run

Action

由于我需要在func testActionRun() { let expect = expectation(description: #function) let sut = ActionRunner() sut.run(action: .doThing) { expect.fulfill() // Assert something } waitForExpectations(timeout: 0.1, handler: nil) } 上测试其他情况,因此我在整个测试套件中传播了很多ActionRunner

问题是:如果我对生产代码进行了更改并将.doThing更改为case doThing,那么现在我的所有测试套件都会失败,因为没有case doThatThing

完美的做法是在测试代码中声明一个虚拟case doThing以允许类似

的内容
case

sut.run(action: .dummyAction) { } 不允许这样做,因为它不允许继承,也不允许扩展添加enum

我想到的第一个选项是将case转换为协议,但这种更改在生产中是不必要的,其唯一目的是在测试代码中完成某些任务。

那么,是否还有其他选择来实现这一目标?

2 个答案:

答案 0 :(得分:2)

使用枚举时如何避免耦合的问题是一个棘手的问题。我自己碰到了几次没有坚实的答案:/

您提出的一点是使用协议,在生产中感觉不必要。我有点同意,但大部分时间都是必要的邪恶。

在您展示的示例中,我认为设计中的调整可能会解决部分问题。

特别是在查看此代码时

func run(action: Action, block: () -> Void) {
    // ...
}

func testActionRun() {

    let expect = expectation(description: #function)
    let sut = ActionRunner()

    sut.run(action: .doThing) {
        expect.fulfill()
        // Assert something
    }

    waitForExpectations(timeout: 0.1, handler: nil)
}

我想到的是,您的Action指定了某种行为。也就是说,当您测试通过run的{​​{1}}方法时,您期望的行为与传递.doThing时的行为不同。

如果这是正确的,是否有任何理由需要将动作枚举实例一个动作块传递给.doOtherThing函数?

您可以将定义行为的代码与执行实际操作的代码分开,直到您已经完成的操作。例如:

run

注意:我实际上并没有编译那段代码,所以如果它有一些错误请耐心等待。它应该提出这个想法。

通过这种方式,您protocol Actionable { var action: () -> () { get } } enum Action: Actionable { case doThing case doOtherThing var action { switch self { case .doThing: return ... case .doOtherThing: return ... } } class ActionRunner { func run(actionable: Actionable) { actionable.action() } } func testActionRun() { let expect = expectation(description: #function) let sut = ActionRunner() sut.run(actionable: FakeActionable()) { expectation.fulfill() } waitForExpectations(timeout: 0.1, handler: nil) } class FakeActionable: Actionable { let action = { } } func testDoThing() { let sut = Action.doThing sut.action() // XCTAssert for the expected effect of the action } 的唯一目的是正确运行给定的ActionRunner,而Actionable枚举仅用于描述不同的操作应该执行的操作。

此示例代码相当于它可以执行的操作,只运行Action个操作,但您可以构建它以实现更高级的行为。

答案 1 :(得分:1)

如果您更改了生产代码,则还必须更改测试代码以测试这些新更改。

也许您可以在Action

的setUp func中的XCTestCase变量上设置值
import XCTest

class SharingKitTests: XCTestCase {
    var theAction: Action!

    override func setUp() {
        super.setUp()

        self.theAction = .doThing

    }
}

然后,您将能够在所有测试方法中使用此 theAction var,如果您需要更改该值,则只需在一个位置更改它。