csdn tdd_具有模拟响应的API客户端TDD

csdn tdd

In parts one and two, we built some very basic functionality and used TDD with PHPUnit to make sure our classes are well tested. We also learned how to test an abstract class in order to make sure its concrete methods worked. Now, let’s continue building our library.

在零件一个2 ,我们建立了一些非常基本的功能和使用的TDD PHPUnit的,以确保我们的课程是行之有效的。 我们还学习了如何测试抽象类,以确保其具体方法有效。 现在,让我们继续构建我们的库。

alt

跟上来 (Catch up)

I took the liberty of implementing the functionality and the test for the abstract API class’ constructor, requiring the URL to be passed in. It’s very similar to what we did with the Diffbot and DiffbotTest classes.

我自由地为抽象API类的构造函数实现功能和测试,要求传递URL。这与我们对Diffbot和DiffbotTest类所做的非常相似。

I also added some more simple methods, and testing of the different API instantiations and custom fields for the APIs into the mix with dynamic setters and getters using __call. This seemed like too menial work to bother you with as it’s highly repetitive and ultimately futile at this point, but if you’re curious, please leave a comment below and we’ll go through part2-end > part3-start differences in another post – you can even diff the various files and ask about specific differences in the forums, I’d be happy to answer them to the best of my knowledge, and also take some advice regarding their design. Additionally, I have moved the “runInSeparateProcess” directive from the entire DiffbotTest class to just the test that needs an empty static class, which reduced the duration of the entire testing phase to mere seconds.

我还添加了一些更简单的方法,并使用__call将动态的setter和getter混合使用来测试这些API的不同API实例化和自定义字段。 这似乎很琐碎,因此很麻烦,因为它是非常重复的,最终毫无用处,但是如果您很好奇,请在下面留下评论,我们将在另一篇文章中介绍part2-end> part3-start差异。 –您甚至可以散布各种文件,并询问论坛中的具体差异,我很乐意尽我所能回答这些问题,并就其设计提出一些建议。 另外,我将“ runInSeparateProcess”指令从整个DiffbotTest类移至仅需要一个空静态类的测试,这将整个测试阶段的持续时间缩短到仅几秒钟。

If you’re just now joining us, please download the part 3 start branch and catch up.

如果您现在才加入我们,请下载第3部分开始分支并赶上来。

数据模拟 (Data Mocking)

We mentioned before we would be data mocking in this part. This might sound more confusing than it is, so allow me to clarify. When we request a URL through Diffbot, we expect a certain result. Like, requesting a specific Amazon product, we expect to get the parsed values for that product. However, if we rely on this live data in our tests, we face two problems:

我们之前提到过,我们将在本部分中进行数据模拟。 这听起来可能比实际更令人困惑,所以请允许我澄清一下。 当我们通过Diffbot请求URL时,我们期望得到一定的结果。 就像请求特定的Amazon产品一样,我们希望获得该产品的解析值。 但是,如果我们在测试中依赖此实时数据,则会面临两个问题:

  1. The tests become slower by X, where X is the time required to fetch the data from Amazon

    X的测试速度变慢,其中X是从Amazon提取数据所需的时间
  2. The data can change and break our tests. Suddenly, some information our tests relied upon before can break due to different values being returned.

    数据可能会更改并破坏我们的测试。 突然,由于返回的值不同,我们的测试之前依赖的某些信息可能会中断。

Because of this, it’s best if we cache the entire response to a given API call offline – headers and all – and use it to fake a response to Guzzle (functionality Guzzle has built in). This way, we can feed Diffbot a fake every time during tests and make sure it gets the same data, thereby giving us consistent results. Matthew Setter wrote about data mocking with Guzzle and PHPUnit before here, if you’d like to take a look.

因此,最好是离线存储对给定API调用的整个响应(标头和全部),并使用它来伪造对Guzzle的响应(Guzzle内置的功能)。 这样,我们可以在测试期间每次为Diffbot提供伪造品,并确保它获得相同的数据,从而为我们提供一致的结果。 如果您想了解一下,Matthew Setter在此处之前曾写过关于使用Guzzle和PHPUnit进行数据模拟的信息。

To get to the testing level we need, we’ll be faking the data that Diffbot returns. Doesn’t this mean that we aren’t effectively testing Diffbot itself but only our ability to parse the data? Exactly, it does. It’s not on us to test Diffbot – Diffbot’s crew does that. What we’re testing here is the ability to initiate API calls and parse the data they return – that’s all.

为了达到我们需要的测试级别,我们将伪造Diffbot返回的数据。 这是否意味着我们没有有效地测试Diffbot本身,而是仅分析数据的能力? 确实如此。 测试Diffbot并不是我们的责任-Diffbot的工作人员会这样做。 我们在这里测试的是启动API调用并解析它们返回的数据的能力,仅此而已。

更新主类 (Updating the Main Class)

First, we need to update the Diffbot class. Our API subclasses will need to know about the token, and about the HTTP client we’re using. To make this possible, we’ll be registering the Diffbot instance within the API subclasses upon their instantiation.

首先,我们需要更新Diffbot类。 我们的API子类将需要了解令牌以及我们正在使用的HTTP客户端。 为了实现这一点,我们将在实例化API子类后将其注册到Diffbot实例中。

First, add the following property to the Api abstract class:

首先,将以下属性添加到Api抽象类中:

/** @var  Diffbot The parent class which spawned this one */
    protected $diffbot;

and then add the following method:

然后添加以下方法:

/**
     * Sets the Diffbot instance on the child class
     * Used to later fetch the token, HTTP client, EntityFactory, etc
     * @param Diffbot $d
     * @return $this
     */
    public function registerDiffbot(Diffbot $d) {
        $this->diffbot = $d;
        return $this;
    }

Then, we need to update the Diffbot class slightly.

然后,我们需要稍微更新Diffbot类。

Add the following content to Diffbot.php:

将以下内容添加到Diffbot.php

// At the top:
use GuzzleHttp\Client;

// As a property:
/** @var Client The HTTP clients to perform requests with */
protected $client;

// Methods:

    /**
     * Sets the client to be used for querying the API endpoints
     *
     * @param Client $client
     * @return $this
     */
    public function setHttpClient(Client $client = null)
    {
        if ($client === null) {
            $client = new Client();
        }
        $this->client = $client;
        return $this;
    }

    /**
     * Returns either the instance of the Guzzle client that has been defined, or null
     * @return Client|null
     */
    public function getHttpClient()
    {
        return $this->client;
    }

    /**
     * Creates a Product API interface
     *
     * @param $url string Url to analyze
     * @return Product
     */
    public function createProductAPI($url)
    {
        $api = new Product($url);
        if (!$this->getHttpClient()) {
            $this->setHttpClient();
        }
        return $api->registerDiffbot($this);
    }

    /**
     * Creates an Article API interface
     *
     * @param $url string Url to analyze
     * @return Article
     */
    public function createArticleAPI($url)
    {
        $api = new Article($url);
        if (!$this->getHttpClient()) {
            $this->setHttpClient();
        }
        return $api->registerDiffbot($this);
    }

    /**
     * Creates an Image API interface
     *
     * @param $url string Url to analyze
     * @return Image
     */
    public function createImageAPI($url)
    {
        $api = new Image($url);
        if (!$this->getHttpClient()) {
            $this->setHttpClient();
        }
        return $api->registerDiffbot($this);
    }

    /**
     * Creates an Analyze API interface
     *
     * @param $url string Url to analyze
     * @return Analyze
     */
    public function createAnalyzeAPI($url)
    {
        $api = new Analyze($url);
        if (!$this->getHttpClient()) {
            $this->setHttpClient();
        }
        return $api->registerDiffbot($this);
    }

We added the ability to set a client, and we made it default to a new instance of Guzzle Client. We also improved direct methods for instantiation of API subtypes, all of which are nearly identical. This is on purpose – we might need specific configuration per API type later on, so separating them out like this will benefit us in the long run. Likewise, it contains some identical code which we’ll later detect and fix with PHPCPD (PHP Copy Paste Detector). The Diffbot class now also injects itself into spawned API classes.

我们添加了设置客户端的功能,并将其默认设置为Guzzle Client的新实例。 我们还改进了用于实例化API子类型的直接方法,所有这些方法几乎都是相同的。 这是有目的的–稍后我们可能需要针对每种API类型进行特定的配置,因此从长远来看,这样将它们分开将对我们有利。 同样,它包含一些相同的代码,我们稍后将使用PHPCPD(PHP复制粘贴检测器)对其进行检测和修复。 Diffbot类现在也将自身注入到衍生的API类中。

工厂和实体 (Factories and Entities)

You might be wondering – isn’t this a good place for the factory pattern? Making an object that’s strictly in charge of creating the APIs and nothing more? Sure, that might be so – but in my opinion, that’s over-engineering it. The Diffbot class was always meant to return new instances of the various APIs, and as such it performs its function through these methods. Likewise, our library has a very specific purpose, and was, from the ground-up, intended to depend heavily on Guzzle. Abstracting too much would get us lost in the complexity of something overly simple and would waste our time. Diffbot is our factory.

您可能想知道–这不是工厂模式的好地方吗? 制作一个严格负责创建API的对象,仅此而已? 当然可以,但我认为这是过度设计的。 Diffbot类始终旨在返回各种API的新实例,因此它通过这些方法执行其功能。 同样,我们的库有一个非常特定的目的,并且从一开始就完全依赖Guzzle。 太多的抽象会使我们迷失于过于简单的复杂性中,并浪费我们的时间。 Diffbot 我们的工厂。

But there’s one thing it cannot and should not do. What we ultimately want the library to be able to do is give us back an object, for example a “Product” object, with accessors that let us read fetched and parsed fields in a fluent, object oriented manner. In other words:

但是有一件事它不能也不应做。 我们最终希望库能够做的就是给我们提供一个对象,例如“ Product”对象,并带有访问器,该访问器使我们能够以流畅的,面向对象的方式读取提取和解析的字段。 换一种说法:

// ... set URL etc
$product = $productApi->call();
echo $product->getOfferPrice();

This time, to make this possible, we’ll need a factory. Why a factory and not just have the API classes instantiate the entities like “Product” themselves? Because someone coming into contact with our library might want to parse the JSON results returned by Diffbot in a different manner – they might want to modify the output so that it matches their database and is compatible with direct insertion, or they might want an easy way to compare it with their own products, for example.

这次,要实现这一点,我们需要一家工厂。 为什么工厂而不是仅让API类实例化诸如“产品”之类的实体本身? 因为与我们的库建立联系的某人可能想以不同的方式解析Diffbot返回的JSON结果–他们可能想要修改输出,使其与数据库匹配并与直接插入兼容,或者他们可能想要一种简单的方法例如,将其与自己的产品进行比较。

To be able to return such entities, they need an interface if they’re to be interchangeable. However, seeing as we know some of their always-on functionality, let’s make an abstract instead. Create src/Abstracts/Entity.php:

为了能够返回此类实体,如果它们是可互换的,则需要一个接口。 但是,由于我们知道它们的某些常开功能,因此让我们作一个抽象。 创建src/Abstracts/Entity.php

<?php

namespace Swader\Diffbot\Abstracts;

use GuzzleHttp\Message\Response;

abstract class Entity
{
    /** @var Response */
    protected $response;

    /** @var  array */
    protected $objects;

    public function __construct(Response $response)
    {
        $this->response = $response;
        $this->objects = $response->json()['objects'][0];
    }

    /**
     * Returns the original response that was passed into the Entity
     * @return Response
     */
    public function getResponse()
    {
        return $this->response;
    }
}

The Diffbot API returns a JSON object with two subobjects: request and objects, as evident by the JSON output from the test drive on the landing page. Guzzle’s Response Message supports outputting JSON data to arrays, but that’s about it. So, this class only has a constructor into which it accepts the response object and then binds the first element of the “objects” field (the one with the meaningful data) to another protected property.

Diffbot API返回带有两个子对象的JSON对象: requestobjects ,从登录页面上的测试驱动器的JSON输出可以明显看出。 Guzzle的Response Message支持将JSON数据输出到数组,仅此而已。 因此,此类仅具有一个构造函数,该类将接受响应对象,然后将“对象”字段的第一个元素(具有有意义数据的元素)绑定到另一个受保护的属性。

In order for the Factory to be interchangeable, let’s give it an interface. Create src/Interfaces/EntityFactory.php:

为了使Factory可互换,让我们为其提供一个接口。 创建src/Interfaces/EntityFactory.php

<?php

namespace Swader\Diffbot\Interfaces;

use GuzzleHttp\Message\Response;

interface EntityFactory
{
    /**
     * Returns the appropriate entity as built by the contents of $response
     * 
     * @param Response $response
     * @return Entity
     */
    public function createAppropriate(Response $response);
}

Now, we can implement it. Create src/Factory/Entity.php:

现在,我们可以实现它。 创建src/Factory/Entity.php

<?php

namespace Swader\Diffbot\Factory;

use GuzzleHttp\Message\Response;
use Swader\Diffbot\Exceptions\DiffbotException;
use Swader\Diffbot\Interfaces\EntityFactory;

class Entity implements EntityFactory
{
    protected $apiEntities = [
        'product' => '\Swader\Diffbot\Entity\Product',
        'article' => '\Swader\Diffbot\Entity\Article',
        'image' => '\Swader\Diffbot\Entity\Image',
        'analyze' => '\Swader\Diffbot\Entity\Analyze',
        '*' => '\Swader\Diffbot\Entity\Wildcard',
    ];

    /**
     * Creates an appropriate Entity from a given Response
     * If no valid Entity can be found for typoe of API, the Wildcard entity is selected
     *
     * @param Response $response
     * @return \Swader\Diffbot\Abstracts\Entity
     * @throws DiffbotException
     */
    public function createAppropriate(Response $response)
    {
        $this->checkResponseFormat($response);

        $arr = $response->json();
        if (isset($this->apiEntities[$arr['request']['api']])) {
            $class = $this->apiEntities[$arr['request']['api']];
        } else {
            $class = $this->apiEntities['*'];
        }
        return new $class($response);
    }

    /**
     * Makes sure the Diffbot response has all the fields it needs to work properly
     *
     * @param Response $response
     * @throws DiffbotException
     */
    protected function checkResponseFormat(Response $response)
    {
        $arr = $response->json();

        if (!isset($arr['objects'])) {
            throw new DiffbotException('Objects property missing - cannot extract entity values');
        }

        if (!isset($arr['request'])) {
            throw new DiffbotException('Request property not found in response!');
        }

        if (!isset($arr['request']['api'])) {
            throw new DiffbotException('API property not found in request property of response!');
        }
    }
}

This is our basic Entity factory. It checks if the response is valid, and then based on that response creates an entity, pushes the response into it, and returns it. If it cannot find a valid entity (for example, the “api” field in the response doesn’t match a key as defined in the apiEntities property), it picks a wildcard entity. Later, we might even choose to upgrade this factory with the ability to change just some or all of the apiEntities pairs, so that users don’t have to write a whole new factory just to try out a different Entity, but let’s leave that be for now. The Factory class needs testing too, of course. To see the test, refer to the source code on Github – link at the bottom of the post.

这是我们的基本实体工厂。 它检查响应是否有效,然后基于该响应创建一个实体,将响应推入该实体,然后将其返回。 如果找不到有效的实体(例如,响应中的“ api”字段与apiEntities属性中定义的键不匹配),则会选择一个通配符实体。 以后,我们甚至可以选择升级该工厂,使其具有仅更改部分或全部apiEntities对的能力,从而使用户不必为了尝试使用另一个Entity而编写一个全新的工厂,而让我们保留目前。 当然,工厂类也需要测试。 要查看测试,请参考Github上的源代码-文章底部的链接。

Finally, we need to build some of those Entities, else it’s all been for naught. For starters, create src/Entity/Product.php. What can we expect from our Product API call? Let’s take a look.

最后,我们需要构建其中一些实体,否则一切就白了。 首先,请创建src/Entity/Product.php 。 我们可以从Product API调用中得到什么? 让我们来看看。

alt

Focusing only on the “root” values inside the “object” property, we can see we instantly get Title, Text, Availability, OfferPrice, and Brand, among others. We could use something like the __call magic method here to automatically discern what we’re looking for in the array, but for clarity, IDE autocompletion, and the possibility of additional parsing, let’s do it manually. Let’s just build those few now – I’ll leave the rest up to you as an exercise. If you’d like to see how I finished it, refer to the source code at the end of the article.

仅关注“对象”属性中的“根”值,我们可以看到我们立即获得了Title,文本,可用性,OfferPrice和Brand等信息。 我们可以在这里使用类似__call magic方法的方法来自动识别我们在数组中寻找的内容,但是为了清楚起见,IDE自动完成功能以及其他解析的可能性,让我们手动进行。 现在让我们来建立这几个人吧,剩下的我会作为练习。 如果您想了解我是如何完成的,请参考本文结尾处的源代码。

<?php

namespace Swader\Diffbot\Entity;

use Swader\Diffbot\Abstracts\Entity;

class Product extends Entity
{
    /**
     * Checks if the product has been determined available
     * @return bool
     */
    public function isAvailable()
    {
        return (bool)$this->objects['availability'];
    }

    /**
     * Returns the product offer price, in USD, as a floating point number
     * @return float
     */
    public function getOfferPrice()
    {
        return (float)trim($this->objects['offerPrice'], '$');
    }

    /**
     * Returns the brand, as determined by Diffbot
     * @return string
     */
    public function getBrand()
    {
        return $this->objects['brand'];
    }

    /**
     * Returns the title, as read by Diffbot
     * @return string
     */
    public function getTitle()
    {
        return $this->objects['title'];
    }
}

You can see here that a potentially good option would be implementing a currency converter into the Product entity, maybe even support injecting different converters. This would then allow users to read back the item price in currencies other than the default.

您可以在此处看到一个潜在的好选择,那就是在Product实体中实现货币转换器,甚至可能支持注入不同的转换器。 然后,这将允许用户以默认以外的其他货币来读取商品价格。

Naturally, the Product entity needs testing, too. For the sake of brevity, I’ll just refer you to the source code at the end of the post.

当然,产品实体也需要测试。 为了简洁起见,在本文的结尾处,我将仅向您推荐源代码。

Finally, we need to add the EntityFactory to the Diffbot instance, similar to what we did with the Guzzle Client:

最后,我们需要将EntityFactory添加到Diffbot实例,类似于我们对Guzzle Client所做的操作:

/**
     * Sets the Entity Factory which will create the Entities from Responses
     * @param EntityFactory $factory
     * @return $this
     */
    public function setEntityFactory(EntityFactory $factory = null)
    {
        if ($factory === null) {
            $factory = new Entity();
        }
        $this->factory = $factory;
        return $this;
    }

    /**
     * Returns the Factory responsible for creating Entities from Responses
     * @return EntityFactory
     */
    public function getEntityFactory()
    {
        return $this->factory;
    }

Don’t forget to add the factory protected property:

不要忘记添加factory保护的属性:

/** @var  EntityFactory The Factory which created Entities from Responses */
    protected $factory;

Now, let’s talk some more about mocking the resources.

现在,让我们进一步讨论模拟资源。

创造嘲弄 (Creating Mocks)

Creating mock response files to be used with Guzzle is very simple (and necessary to be able to test our classes). They need to look something like this – only with a JSON body instead of XML. This is easily accomplished with cURL. First, create the folder tests/Mocks/Products. Use the terminal to enter it, and execute the following command:

创建要与Guzzle一起使用的模拟响应文件非常简单(这对于测试我们的类是必要的)。 他们需要看起来像这样 -仅使用JSON主体而不是XML。 使用cURL可以轻松完成此操作。 首先,创建文件夹tests/Mocks/Products 。 使用终端输入它,并执行以下命令:

curl -i "http://api.diffbot.com/v3/product?url=http%3A%2F%2Fwww.petsmar t.com%2Fdog%2Fgrooming-supplies%2Fgrreat-choice-soft-slicker-dog-brush-zid36-12094%2Fcat-36-catid-100016&token =demo&fields=saveAmount,mpn,prefixCode,meta,sku,queryString,saveAmountDetails,shippingAmount,productOrigin,regularPriceDetails,offerPriceDetails" > dogbrush.json

Opening the dogbrush.json file will show you the full content of the response – both headers and body.

打开dogbrush.json文件将为您显示响应的完整内容-标头和正文。

测试通话 (Testing the Call)

Now that we have our dogbrush response, and all our classes are ready, we can use it to test the Product API. For that, we need to develop the call method. Once the call is executed, we expect to get back some values that Diffbot parsed, and we expect them to be correct.

既然我们有了dogbrush响应,并且所有类都已准备就绪,我们就可以使用它来测试Product API。 为此,我们需要开发call方法。 执行完调用后,我们希望取回Diffbot解析的某些值,并且希望它们是正确的。

We start with the test. Edit ProductApiTest.php to look like this:

我们从测试开始。 编辑ProductApiTest.php如下所示:

<?php

namespace Swader\Diffbot\Test\Api;

use GuzzleHttp\Client;
use GuzzleHttp\Subscriber\Mock;
use Swader\Diffbot\Diffbot;

class ProductApiTest extends \PHPUnit_Framework_TestCase
{

    protected $validMock;

    protected function getValidDiffbotInstance()
    {
        return new Diffbot('demo');
    }

    protected function getValidMock(){
        if (!$this->validMock) {
            $this->validMock = new Mock(
                [file_get_contents(__DIR__.'/../Mocks/Products/dogbrush.json')]
            );
        }
        return $this->validMock;
    }

    public function testCall() {
        $diffbot = $this->getValidDiffbotInstance();

        $fakeClient = new Client();
        $fakeClient->getEmitter()->attach($this->getValidMock());

        $diffbot->setHttpClient($fakeClient);
        $diffbot->setEntityFactory();

        $api = $diffbot->createProductAPI('https://dogbrush-mock.com');

        /** @var Product $product */
        $product = $api->call();

        $targetTitle = 'Grreat Choice® Soft Slicker Dog Brush';
        $this->assertEquals($targetTitle, $product->getTitle());
        $this->assertTrue($product->isAvailable());
        $this->assertEquals(4.99, $product->getOfferPrice());
        $this->assertEquals('Grreat Choice', $product->getBrand());
    }

}

We get a valid Diffbot instance first, create and inject a faked Guzzle client with our previously downloaded response. We set the Entity Factory, and after we call call, we expect a specific title to be returned. Should this assertion not hold, the test will fail. We also test other properties for which we know true values.

我们首先获得一个有效的Diffbot实例,并使用我们先前下载的响应创建并注入伪造的Guzzle客户端。 我们设置了实体工厂,并在调用call ,期望返回特定的标题。 如果此断言不成立,则测试将失败。 我们还测试了我们知道真实值的其他属性。

Now we need to develop the call method. It will be identical for all our APIs (they all do the same thing, essentially – a remote request to a given URL), so we put it into the abstract Api class:

现在我们需要开发call方法。 我们所有的API都是相同的(本质上,它们都做同样的事情–对给定URL的远程请求),因此我们将其放入抽象的Api类中:

public function call()
    {
        $response = $this->diffbot->getHttpClient()->get($this->buildUrl());
        return $this->diffbot->getEntityFactory()->createAppropriate($response);
    }

You can see here we’re using a buildUrl method. This method will use all the custom fields of an API to enhance a default URL and pass the fields along with the request, in order to return the additional values we’ve requested.

您可以在这里看到我们正在使用buildUrl方法。 此方法将使用API​​的所有自定义字段来增强默认URL,并将这些字段与请求一起传递,以返回我们请求的其他值。

First, we write some tests for it in ProductApiTest, so that we know the build works for the given API type:

首先,我们在ProductApiTest中为其编写一些测试,以便我们知道该构建适用于给定的API类型:

public function testBuildUrlNoCustomFields() {
        $url = $this
            ->apiWithMock
            ->buildUrl();
        $expectedUrl = 'http://api.diffbot.com/v3/product/?token=demo&url=https%3A%2F%2Fdogbrush-mock.com';
        $this->assertEquals($expectedUrl, $url);
    }

    public function testBuildUrlOneCustomField() {
        $url = $this
            ->apiWithMock
            ->setOfferPriceDetails(true)
            ->buildUrl();
        $expectedUrl = 'http://api.diffbot.com/v3/product/?token=demo&url=https%3A%2F%2Fdogbrush-mock.com&fields=offerPriceDetails';
        $this->assertEquals($expectedUrl, $url);
    }

    public function testBuildUrlTwoCustomFields() {
        $url = $this
            ->apiWithMock
            ->setOfferPriceDetails(true)
            ->setSku(true)
            ->buildUrl();
        $expectedUrl = 'http://api.diffbot.com/v3/product/?token=demo&url=https%3A%2F%2Fdogbrush-mock.com&fields=sku,offerPriceDetails';
        $this->assertEquals($expectedUrl, $url);
    }

Here is the method in full:

这是完整的方法:

protected function buildUrl()
    {
        $url = rtrim($this->apiUrl, '/') . '/';

        // Add Token
        $url .= '?token=' . $this->diffbot->getToken();

        // Add URL
        $url .= '&url='.urlencode($this->url);

        // Add Custom Fields
        $fields = static::getOptionalFields();
        $fieldString = '';
        foreach ($fields as $field) {
            $methodName = 'get' . ucfirst($field);
            $fieldString .= ($this->$methodName()) ? $field . ',' : '';
        }
        $fieldString = trim($fieldString, ',');
        if ($fieldString != '') {
            $url .= '&fields=' . $fieldString;
        }

        return $url;
    }

If we run the test now, everything should pass:

如果我们现在运行测试,一切都将通过:

alt

结论 (Conclusion)

In this part, we did some more TDD, moving in the direction of completion. Due to the extensiveness of the content that can be written on the topic of testing, I’ve decided to cut the story short here. We should definitely test for whether or not the custom fields work, good URLs are built, and so on and so forth – the areas you can test are nigh infinite, but it would unnecessarily extend the tutorial beyond one’s attention span. You can always see the fully finished result on Github. If, however, you’re interested in reading a tutorial on the rest of the logic, please do let me know and I’ll do my best to continue explaining, piece by piece.

在这一部分,我们朝着完成的方向进行了更多的TDD。 由于可以在测试主题上编写的内容广泛,因此我决定在这里简化故事。 我们绝对应该测试自定义字段是否起作用,是否构建了良好的URL等,等等–您可以测试的区域几乎是无限的,但是这会不必要地使教程超出人们的注意力范围。 您始终可以在Github上看到完整的结果。 但是,如果您有兴趣阅读有关其余逻辑的教程,请告诉我,我将尽我所能继续逐一进行解释。

Don’t forget to implement the other entities and tests as “homework”! It seems like a waste of time, but in time you’ll grow to love the security tests give you in the long run, I promise! Likewise, if you have any ideas on improving my approaches, I’m always open to learning something new and will gladly take a look at pull requests and constructive feedback.

不要忘记将其他实体和测试实现为“作业”! 看来,这似乎是在浪费时间,但是随着时间的流逝,您会逐渐喜欢上安全测试,从长远来看,它可以保证您的安全! 同样,如果您对改进我的方法有任何想法,我总是乐于学习新知识,并乐于研究拉取请求和建设性反馈。

In the next and final part, we’ll wrap things up and deploy our package to Packagist.org so everyone can install it at will via Composer.

在下一部分,也是最后一部分,我们将打包并将程序包部署到Packagist.org,以便每个人都可以通过Composer随意安装它。

翻译自: https://www.sitepoint.com/api-client-tdd-mocked-responses/

csdn tdd

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值