我读过各种关于测试中模仿和存根的文章,包括Martin Fowler的《Mocks Aren't Stubs》,但我仍然不理解其中的区别。


当前回答

他使用的通用术语是测试替身(想想特技替身)。Test Double是一个通用术语,用于为测试目的替换生产对象的任何情况。杰拉德列出了各种各样的替身:

Dummy objects are passed around but never actually used. Usually they are just used to fill parameter lists. Fake objects actually have working implementations, but usually take some shortcut which makes them not suitable for production (an InMemoryTestDatabase is a good example). Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test. Spies are stubs that also record some information based on how they were called. One form of this might be an email service that records how many messages it was sent(also called Partial Mock). Mocks are pre-programmed with expectations which form a specification of the calls they are expected to receive. They can throw an exception if they receive a call they don't expect and are checked during verification to ensure they got all the calls they were expecting.

其他回答

存根是向SUT返回值的测试double。

模拟是测试用于验证SUT正确调用依赖项的双重测试。

而且,mock通常是存根

在我的回答中,我使用了python示例来说明差异。

Stub - Stubbing is a software development technique used to implement methods of classes early in the development life-cycle. They are used commonly as placeholders for implementation of a known interface, where the interface is finalized or known but the implementation is not yet known or finalized. You begin with stubs, which simply means that you only write the definition of a function down and leave the actual code for later. The advantage is that you won't forget methods and you can continue to think about your design while seeing it in code. You can also have your stub return a static response so that the response can be used by other parts of your code immediately. Stub objects provide a valid response, but it's static no matter what input you pass in, you'll always get the same response:

class Foo(object):
    def bar1(self):
        pass

    def bar2(self):
        #or ...
        raise NotImplementedError

    def bar3(self):
        #or return dummy data
        return "Dummy Data"

模拟对象用于模拟测试用例,它们验证在这些对象上调用了某些方法。模拟对象是以可控的方式模拟真实对象行为的模拟对象。您通常创建一个模拟对象来测试其他对象的行为。mock让我们模拟对于单元测试来说不可用或太笨重的资源。

mymodule.py:

import os
import os.path

def rm(filename):
    if os.path.isfile(filename):
        os.remove(filename)

test.py:

from mymodule import rm
import mock
import unittest

class RmTestCase(unittest.TestCase):
    @mock.patch('mymodule.os')
    def test_rm(self, mock_os):
        rm("any path")
        # test that rm called os.remove with the right parameters
        mock_os.remove.assert_called_with("any path")

if __name__ == '__main__':
    unittest.main()

这是一个非常基本的示例,它只运行rm并断言调用它的参数。您可以对对象使用mock,而不仅仅是这里所示的函数,您还可以返回一个值,这样模拟对象就可以用来替换存根进行测试。

更多关于unittest的信息。模拟,注意python 2。X mock不包含在unittest中,但它是一个可下载的模块,可以通过PIP (PIP install mock)下载。

我还读过Roy Osherove写的《单元测试的艺术》,我认为如果有一本类似的书是用Python和Python示例编写的,那就太棒了。如果有人知道这样的书,请分享。欢呼:)

看了上面所有的解释,让我试着总结一下:

Stub:让测试运行的一段虚拟代码,但您并不关心它会发生什么。替代实际工作代码。 Mock:在测试中验证是否正确调用的一段虚拟代码。替代实际工作代码。 间谍:一段虚拟代码,用于拦截和验证对实际工作代码的某些调用,从而避免替换所有实际代码。

测试双打:

Fake: Fakes are objects that have working implementations, but not the same as production one. Such as: in-memory implementation of Data Access Object or Repository. Stub: Stub is an object that holds predefined data and uses it to answer calls during tests. Such as: an object that needs to grab some data from the database to respond to a method call. Mocks: Mocks are objects that register calls they receive. In test assertion, we can verify on Mocks that all expected actions were performed. Such as: a functionality that calls e-mail sending service. for more just check this.

存根和模拟都覆盖外部依赖项,但区别在于

stub ->用于测试数据

mock ->用于测试行为


不测试任何东西(只是用空方法覆盖功能,例如替换Logger以避免在测试时记录任何噪音)