测试实例中的方法是否已在模拟

时间:2017-12-02 10:11:19

标签: python python-2.7 unit-testing mocking

我有这种设置,我正在测试一个使用另一个类的类,我想模仿后者,所以我只测试第一个类本身。

nuclear_reactor.py

class NuclearReactor():
    def __init__(self):
        print "initializing the nuclear reactor"

    def start(self):
        print "starting the nuclear reactor"

nuclear_manager.py

from nuclear_reactor import NuclearReactor

class NuclearManager():
    def __init__(self):
        print "manager creating the nuclear reactor"
        self.reactor = NuclearReactor()

    def start(self):
        print "manager starting the nuclear reactor"
        self.reactor.start()

test_nuclear_manager.py

from mock import Mock
import nuclear_manager
from nuclear_manager import NuclearManager

def test():
    mock_reactor = nuclear_manager.NuclearReactor = Mock()
    nuke = NuclearManager()
    nuke.start()
    nuke.start()
    print mock_reactor.mock_calls
    print mock_reactor.start.call_count

test()

我想测试的是NuclearReactor.start被调用, 但是当我跑步时,我得到了:

manager creating the nuclear reactor
manager starting the nuclear reactor
manager starting the nuclear reactor
[call(), call().start(), call().start()]
0

我完全理解,因为start是实例的属性而不是类的属性,我可以解析mock_calls,但是没有更好的方法来检查调用是一个实例化的模拟类?

我可以在NuclearManager中使用依赖注入来传递模拟NuclearReactor,但我认为只使用模拟会有另一种方法。

2 个答案:

答案 0 :(得分:5)

您确实正在测试start是否已直接在类上调用,而您的代码却没有。您可以直接在实例上测试该方法;请记住,通过调用类来生成实例:

print mock_reactor.return_value.calls
print mock_reactor.return_value.start.call_count

Mock.return_value attribute是对模拟类的调用的结果,因此是实例。

你也可以调用模拟。默认情况下,Mocks在调用时总是返回完全相同的对象,一个表示该返回值的新模拟:

print mock_reactor().calls
print mock_reactor().start.call_count

调用模拟实例的结果和模拟实例return_value属性是同一个。

通过打印出对NuclearReactor模拟的调用,您已经走在正确的道路上了,您错过了在名为模拟器上调用start()的详细信息,所以call().start(),而非start()已被记录。

您可能希望使用mock.patch()来处理修补,而不是直接分配;这样可以确保再次删除补丁,以便其他测试可以自行决定被模拟的内容:

import mock
from nuclear_manager import NuclearManager

@mock.patch('nuclear_manager.NuclearReactor')
def test(mock_reactor):
    nuke = NuclearManager()
    nuke.start()
    nuke.start()

    instance = mock_reactor.return_value
    assert instance.start.call_count == 2
    instance.assert_called()

我在这里用作装饰器;当调用test()函数时,模拟就会到位,当函数退出时,它会被再次删除。您还可以使用patch()作为上下文管理器,以更加精细地限制补丁的范围。

此外,对于像这样的单元测试,请使用unittest library

import mock
import unittest
import nuclear_manager

class NuclearManagerTests(unittest.TestCase):
    @mock.patch('nuclear_manager.NuclearReactor')
    def test_start(self, mock_reactor):
        nuke = NuclearManager()
        nuke.start()
        nuke.start()

        instance = mock_reactor.return_value
        self.assertEqual(instance.start.call_count, 2)
        instance.assert_called()

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

这使您可以将测试适合更大的测试套件,启用和禁用测试,以及与其他测试工具集成。

答案 1 :(得分:-2)

我使用模拟的方式是这样的:(代码是Python 3)

@NgModule({
  providers: [
    { provide: HTTP_INTERCEPTORS, useClass: XxxInterceptor, multi: true },
  ],
})
export class XxxModule {
}

@NgModule({
  declarations: [
  ],
  imports: [
    XxxModule,
    HttpClientJsonpModule,
  ],
  providers: [
    ...
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

输出:

public string SendWebRequestSinProxy(string url)
    {          
            //controlarTiempos("SendWebRequestSinProxy(string url)");                
            string result = "";
            using (HttpClient client = new HttpClient())
            {                
            client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "text/html; charset=utf-8");                
            client.Timeout.Add(new TimeSpan(1, 0, 0));

                 using (HttpResponseMessage response = client.GetAsync(url).Result)
                 {
                        using (HttpContent content = response.Content)
                        {                                
                            result = content.ReadAsStringAsync().Result;
                        }
                  }
            }            
        return result;
    }