使用 Rspec 和 C 扩展进行单元测试

Unit testing with Rspec and C extensions

这些天似乎每个人都在谈论 TDD 和 BDD,所以我想我尝试一下小型家庭项目。

一个简短的背景 我正在用 C 扩展开发 class Device,它与本机 C API 交互,用于控制电灯开关等远程设备。

为了让测试更独立于平台,我已经模拟了 C API 来表现得像原来的那样,但它没有控制硬件,而是设置了一个全局 ruby 状态变量(一个哈希包含所有设备).

这一切都很好并且按预期工作。我可以编译并 link 到这个库而不是真正的库。

现在我需要帮助的部分是如何按照某种最佳实践进行测试 我有以下规范来描述我的 Device class

  describe Device do
    describe ".get" do
      context "Specified id exists" do
        it "Returns a instance of Device"
        it "Device has same id as specified"
      end
      context "Specified id is undefined"
        it "throws UndefinedDeviceException"
      end
    end
    describe "#turn_on" do
      context "Device supports turning on" do
        # I set up the mock state to have a device that supports on..
        # how do i get the Device instance here? is it ok to depend on
        # Device.get method?
        it "Returns true"
        it "Set device state to on" # Check mocked state
      end
    end
    .....
  end

现在,据我所知,在每个上下文中,我为我的模拟库设置状态以反映我想要测试的内容,并调用该方法。 这适用于静态方法 Device.get(id) 因为这个 returns 一个新的 Device 我不确定如何测试实例方法。如果我想测试 turn_on 方法,我首先需要有一个 Device 的实例,这意味着我需要调用 Device.get 来实际获取设备?但如果是这样的话,我是不是又 "testing" class 方法得到了?我想我在这里真正要问的是,如果它依赖于外部库,那么在 Rspec 中获取对象实例是什么好的做法。

调用 Device.get 听起来很适合我 - 在这些测试中,您将测试返回的 Device 对象的某些方法,而不是 get 本身。

虽然测试应该是独立的,但您不可避免地会得到一些使用其他测试测试的方法的测试 - 例如,活动记录测试套件有大量 create 和 [=12 的测试=] 但这些方法也被许多其他测试使用。

这方面的棘手之处在于决定在什么级别进行模拟 - 您最终可以只测试您的模拟和 none(或很少)实际实施。