夜神模拟器模拟安卓测试_模拟对象测试简介

夜神模拟器模拟安卓测试

If you are part of a development team, more often than not your code will also depend on code written by your teammates. But what if their code isn’t available at the moment — say, your teammate hasn’t finished writing it yet? Or, what if the code you need requires other external dependencies that are hard to set up? And what if you cannot test code because of other factors beyond your control? Will you just hang around, do nothing, and wait until your team is done or when everything is in place? Of course not!

如果您是开发团队的一员,那么您的代码通常还取决于团队成员编写的代码。 但是,如果目前他们的代码不可用-例如,您的队友尚未完成编写该怎么办? 或者,如果您需要的代码需要其他难以设置的外部依赖项,该怎么办? 如果由于其他无法控制的因素而无法测试代码怎么办? 您会只是闲逛,什么也不做,等到您的团队完成工作或一切就绪后再进行工作吗? 当然不是!

In this this article, I’ll illustrate how to write code that works around this problem with dependencies. Some background on unit testing is ideal, and there is already a great introductory article here on SitePoint on unit testing with written by Michelle Saver. And although it’s not required for this article, do check out my other articles on automated database testing.

在本文中,我将说明如何编写可解决依赖关系问题的代码。 单元测试的一些背景是理想的,并且在SitePoint上已经有一篇由Michelle Saver撰写的有关单元测试的精彩介绍文章 。 而且,尽管本文不是必需的,但请查阅我有关自动数据库测试的其他文章

模拟对象案例 (The Case for Mock Objects)

As you may already have guessed, mock objects are a way out of the sticky situations I mentioned in the introduction. But what are mock objects? Mock objects are objects that stand in for the real implementation of an actual object.

正如您可能已经猜到的,模拟对象是我在引言中提到的那种棘手情况的一种解决方法。 但是什么是模拟对象? 模拟对象是代表实际对象的实际实现的对象。

Now why would you want a stand-in object instead of a real one?

现在,为什么要使用替代对象而不是真实对象?

Mock objects are used in unit-testing to mimic the behavior of real objects in test cases. By using them the functionality of the object you are implementing would be easier to test. Here are some situations where the use of a mock object is beneficial:

模拟对象用于单元测试中,以模仿测试用例中真实对象的行为。 通过使用它们,您实现的对象的功能将更易于测试。 在某些情况下,使用模拟对象是有好处的:

  1. The real implementation of one or more dependencies of an object has not been realized yet. Let’s say you’re tasked to do some processing on some data from a database. You would probably call on a method of some form of data access object or data repository, but what if the database has not been setup yet? What if the there was no data available (a situation I’ve encountered one too many times) or if the code that queries the database hasn’t been written yet? A mock data access object simulates the real data access object by returning some pre-defined values. This frees you from the burden of having to setup the database, look for data or write the code that queries the database.

    尚未实现对象的一个​​或多个依赖项的实际实现。 假设您的任务是对数据库中的某些数据进行一些处理。 您可能会调用某种形式的数据访问对象或数据存储库的方法,但是如果尚未设置数据库怎么办? 如果没有可用的数据(一种情况,我已经遇到过太多次),或者尚未编写查询数据库的代码该怎么办? 模拟数据访问对象通过返回一些预定义的值来模拟实际数据访问对象。 这使您免于设置数据库,查找数据或编写查询数据库的代码的负担。

  2. The real implementation of the dependencies of an object depends on factors that are hard to simulate. Suppose you would want to tabulate the likes and comments of a Facebook post by day. Where will you get your data? Your Facebook developer account is new. Heck, you still do not have friends! How will you simulate likes and comments? Mock objects offer a better approach than bothering your fellow developers to like or comment on some posts for you. And how will you simulate all these actions in a range of days if you wanted to show data by day? How about by month? What do you do if the unthinkable happens and Facebook is down at the moment? A mock object can pretend to be the Facebook library and return the data you need. You don’t have to go through the hassles I just mentioned just to get started working on your task.

    对象依赖关系的实际实现取决于难以模拟的因素。 假设您想按天列出Facebook帖子的喜欢和评论。 您将在哪里获取数据? 您的Facebook开发者帐户是新帐户。 哎呀,你仍然没有朋友! 您将如何模拟喜欢和评论? 模拟对象提供了一种更好的方法,比麻烦您的开发人员为您喜欢或评论某些帖子提供了更好的方法。 如果要每天显示数据,如何在几天内模拟所有这些动作? 每月如何? 如果无法想象的事情发生了,而Facebook现在倒闭了,您该怎么办? 模拟对象可以伪装成Facebook库,并返回您需要的数据。 您不必经历我刚刚提到的麻烦就可以开始执行您的任务。

模拟对象 (Mock Objects in Action)

Now that we know what mock objects are, let’s see some examples in action. We’ll implement our simple functionality previously mentioned, e.g tabulating likes and comments of a Facebook post.

现在我们知道了什么是模拟对象,让我们来看一些实际的示例。 我们将实现前面提到的简单功能,例如,将Facebook帖子的喜欢和评论列表化。

We’ll start with the following unit test to define our expectations of how our object is going to be called and how what the return value will look like:

我们将从以下单元测试开始,以定义我们对如何调用对象以及返回值的外观的期望:

<?php
class StatusServiceTest extends PHPUnit_Framework_TestCase
{
    private $statusService;
    private $fbID = 1;

    public function setUp() {
        $this->statusService = new StatusService();
    }

    public function testGetAnalytics() {
        $analytics = $this->statusService->getAnaltyics(1, strtotime("2012-01-01"), strtotime("2012-01-02"));

        $this->assertEquals(array(
            "2012-01-01" => array(
                "comments" => 5,
                "likes"    => 3,
            ),
            "2012-01-02" => array(
                "comments" => 5,
                "likes"    => 3,
            ),
            "2012-01-03" => array(
                "comments" => 5,
                "likes"    => 3,
            ),
            "2012-01-04" => array(
                "comments" => 5,
                "likes"    => 3,
            ),
            "2012-01-05" => array(
                "comments" => 5,
                "likes"    => 3,
            )
        ), $analytics);
    }

}

If you run this test you will get a failure. This is expected, as we have not implemented anything yet!

如果运行此测试,将会失败。 这是预料之中的,因为我们还没有实现任何东西!

Now let’s write up our implementation of the service. Of course the first step is to get the data from Facebook, so let us to do that first:

现在,让我们来编写我们的服务实现。 当然,第一步是从Facebook获取数据,所以让我们首先这样做:

<?php
class StatuService
{
    private $facebook;

    public function getAnalytics($id, $from, $to) {
        $post = $this->facebook->get($id);
    }
}

This test too will fail because the Facebook object is null. You could plug in an actual implementation creating an actual instance with a Facebook app ID etc., but what for? We already all know what agony is involved to do that when it let’s you deviate from the task at hand. We can spare ourselves by injecting a mock one instead!

该测试也会失败,因为Facebook对象为null。 您可以插入一个实际实现,使用Facebook应用程序ID等创建一个实际实例,但是这是为了什么呢? 当您让它偏离手头的任务时,我们都已经知道这样做会带来多大的痛苦。 我们可以通过注入一个模拟的来代替自己!

The way this is done using mock objects, at least in our case, is to create a class that has a get() method and returns mock values. This should fool our client into thinking that it is calling the real object implementation when in fact it was actually just mocked.

至少在我们的例子中,使用模拟对象完成此操作的方式是创建一个具有get()方法并返回模拟值的类。 这应该使我们的客户误以为实际上它只是在被嘲笑,而是在调用真实的对象实现。

<?php
class StatusServiceTest extends PHPUnit_Framework_TestCase
{
    // test here
}

class MockFacebookLibrary
{
    public function get($id) {
        return array(
            // mock return from Facebook here
        );
    }
}

Now that we have a mock class, let’s instantiate an instance and then inject it into StatusService so that it can be used. But first, update StatusService with a setter for the Facebook library:

现在我们有了一个模拟类,让我们实例化一个实例,然后将其注入到StatusService以便可以使用它。 但首先,使用Facebook库的设置器更新StatusService

<?php
class StatusService
{
    // other lines of code

    public function setFacebook($facebook) {
        $this->facebook = facebook;
    }
}

Now inject the mock Facebook library:

现在注入模拟Facebook库:

<?php
class StatusServiceTest extends PHPUnit_Framework_TestCase
{
    public function testGetAnalytics {
        $analytics = $this->statusService->getAnaltyics(1, strtotime("2012-01-01"), strtotime("2012-01-02"));
        $analytics->setFacebook(new MockFacebook());

        // code truncated
    }
}

The test still fails, but at least we don’t get the error related to calling a method on a non-object anymore. More importantly though, you have just addressed the need to fulfill this dependency. You can now start programming the business logic you are tasked to do and pass the test.

测试仍然失败,但是至少我们不再收到与在非对象上调用方法有关的错误。 但是,更重要的是,您刚刚解决了实现此依赖关系的需求。 现在,您可以开始对要执行的业务逻辑进行编程,并通过测试。

PHPUnit 3.6.7 by Sebastian Bergmann.
F
Time: 0 seconds, Memory: 3.00Mb
There was 1 failure:
1) StatusServiceTest::testGetAnalytics null does not match expected type "array".
/home/jeune/mock-object-tutorial/ManualMockStatusServiceTest.php:43
FAILURES! Tests: 1, Assertions: 1, Failures: 1.

更进一步:使用模拟框架 (Taking it a Step Further: Using a Mocking Framework)

While certainly you can live with using hand-crafted mocks when you are just starting out, later on, as I found out myself, you might have the need to use a real mocking framework as your needs become more complicated. In this article, I’ll show how to use the mocking framework that comes with PHPUnit.

虽然当然可以在刚开始时就使用手工制作的模拟程序,但是后来,正如我发现的那样,随着您的需求变得越来越复杂,您可能需要使用一个真正的模拟框架。 在本文中,我将展示如何使用PHPUnit附带的模拟框架。

In my experience, here are some benefits to using a mock framework over using manually written mocks:

以我的经验,与使用手动编写的模拟相比,使用模拟框架有一些好处:

  • You can afford to be lazy. I found this especially true if you are dealing with abstract classes with lots of abstract methods. You can afford to mock only certain methods of an abstract class or interface. If you were doing this manually, then you would have to implement all of them by hand. It saves you some typing and comes pre-packaged with type hinting as well; you only setup what you need and you don’t have to maintain a new class for each test case.

    您可以懒惰。 如果您要处理具有许多抽象方法的抽象类,我会发现这尤其正确。 您只能模拟抽象类或接口的某些方法。 如果您手动执行此操作,则必须手动实施所有这些操作。 它为您节省了一些键入操作,并且还预先包装了类型提示。 您只需设置所需的内容,而不必为每个测试用例维护一个新的类。

  • You can write cleaner code. Readability is the key here. Framework-based mocks make your tests easier to understand as your mocks are written in the test. You do not need scroll down or switch between files to view hand-written mocks written elsewhere. What if you needed to call your mock object more than once with different results? With framework-based mocks, the if-else boiler plate code required to do this is already well encapsulated. Hence, it is easier on the eyes.

    您可以编写更简洁的代码。 可读性是这里的关键。 基于框架的模拟使您的测试更容易理解,因为模拟是在测试中编写的。 您无需向下滚动或在文件之间切换即可查看其他地方编写的手写模拟。 如果您需要多次调用模拟对象并获得不同的结果怎么办? 使用基于框架的模拟,已经很好地封装了执行此操作所需的if-else样板代码。 因此,在眼睛上更容易。

使用PHPUnit的模拟框架 (Using PHPUnit’s Mocking Framework)

Turning our attention to using PHPUnit’s Mocking Framework, the steps are actually pretty intuitive and, once you get the hang of it, it will be second nature. In this section we’ll be using PHPUnit’s Mocking Framework to create a mock object for our example situation.

让我们将注意力转向使用PHPUnit的Mocking Framework,实际上这些步骤非常直观,一旦掌握了这些技巧,这将是第二自然。 在本节中,我们将使用PHPUnit的Mocking Framework为我们的示例情况创建一个模拟对象。

Before we do that however comment out or remove the line that uses our hand-crafted mock object in our test. We need to fail first so we have something to pass. Later on, we will inject our new mock implementation.

但是,在执行此操作之前,请注释掉或删除在测试中使用手工制作的模拟对象的行。 我们需要首先失败,这样我们才能有所作为。 稍后,我们将注入新的模拟实现。

<?php
class StatusServiceTest extends PHPUnit_Framework_TestCase
{
    public function testGetAnalytics {
        $analytics = $this->statusService->getAnaltyics(1, strtotime("2012-01-01"), strtotime("2012-01-02"));
        //$analytics->setFacebook(new MockFacebook());

        // code truncated
    }
}

Verify that the test indeed fails when you run PHPUnit.

运行PHPUnit时,验证测试确实失败。

Now, think about how we mocked an object and the method manually that we want to invoke. What did we do?

现在,考虑一下我们如何手动模拟要调用的对象和方法。 我们做了什么?

  1. The first step is to identify what objects to mock. In our analytics example feature above, we mocked the Facebook library. We are doing the same thing as a first step.

    第一步是确定要模拟的对象。 在上面的分析示例功能中,我们模拟了Facebook库。 第一步,我们正在做同样的事情。

  2. Now that we have defined what class to mock, we have to know what methods in the class we want to mock specifying parameters and return values if there are any. A basic template I use in most cases go somewhat like this:

    现在,我们定义了要模拟的类,我们必须知道要模拟的类中的哪些方法来指定参数并返回值(如果有)。 我在大多数情况下使用的基本模板如下所示:

  1. Specify the number of times a method will be called (Required).

    指定方法被调用的次数(必需)。
  2. Specify the method name (Required).

    指定方法名称(必填)。
  3. Specify the parameters that the method expects (Optional).

    指定方法期望的参数(可选)。
  4. Specify the return value (Optional)

    指定返回值(可选)

Let’s apply the steps just mentioned to our example test.

让我们将刚才提到的步骤应用于示例测试。

<?php
    //code truncated
    public function testGetAnalytics()
    {
        $arr = array(// facebook return);

        $facebook = $this->getMock('facebook') // object to mock
            ->expects($this->once())    // number of times to be called
            ->method('get') // method name
            ->with(1) // parameters that are expected
            ->will($this->returnValue($arr)); // return value

            // code truncated
    }

After we have created our mock facebook object again, inject it back again into our service:

再次创建模拟Facebook对象之后,将其再次注入我们的服务中:

<?php
    //code truncated
    public function testGetAnalytics()
    {
        $arr = array(// facebook return);

        $facebook = $this->getMock('facebook') // object to mock
            ->expects($this->once())    // number of times to be called
            ->method('get') // method name
            ->with(1) // parameters that are expected
            ->will($this->returnValue($arr)); // return value

        $this->statusService->setFacebook($facebook);

        // code truncated
    }

Now, you should have a passing test again. Congratulations! You have hit the ground running with using mock objects for testing! Hopefully you should be able to program more efficiently and most of all break free from show stopper dependencies you run into in the future.

现在,您应该再次通过测试。 恭喜你! 您已经使用模拟对象进行测试了! 希望您应该能够更有效地进行编程,并且最重要的是将来摆脱遇到的显示停止器依赖性。

Image via Fotolia

图片来自Fotolia

翻译自: https://www.sitepoint.com/an-introduction-to-mock-object-testing/

夜神模拟器模拟安卓测试

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值