symfony连接数据库_使用Symfony Flex构建图像库博客:数据测试

symfony连接数据库

This article is part of a series on building a sample application — a multi-image gallery blog — for performance benchmarking and optimizations. (View the repo here.)

本文是构建用于性能基准测试和优化的示例应用程序(一个多图像画廊博客)系列文章的一部分。 (在此处查看回购 。)



In the previous article, we demonstrated how to set up a Symfony project from scratch with Flex, and how to create a simple set of fixtures and get the project up and running.

上一篇文章中 ,我们演示了如何使用Flex从头开始建立Symfony项目,以及如何创建一组简单的夹具并启动和运行该项目。

The next step on our journey is to populate the database with a somewhat realistic amount of data to test application performance.

我们旅程的下一步是用一些实际数据填充数据库以测试应用程序性能。

Note: if you did the “Getting started with the app” step in the previous post, you’ve already followed the steps outlined in this post. If that’s the case, use this post as an explainer on how it was done.

注意:如果您已完成上一篇文章中的“ 使用该应用程序入门 ”步骤,则您已经按照本文中概述的步骤进行操作。 如果是这种情况,请以这篇文章为例进行说明。

As a bonus, we’ll demonstrate how to set up a simple PHPUnit test suite with basic smoke tests.

作为奖励,我们将演示如何使用基本的烟雾测试设置简单PHPUnit测试套件。

更多假数据 (More Fake Data)

Once your entities are polished, and you’ve had your “That’s it! I’m done!” moment, it’s a perfect time to create a more significant dataset that can be used for further testing and preparing the app for production.

完成实体的修饰后,您就拥有了“就是这样! 我受够了!” 此刻,现在是创建更重要的数据集的最佳时机,该数据集可用于进一步测试和为生产准备应用程序。

Simple fixtures like the ones we created in the previous article are great for the development phase, where loading ~30 entities is done quickly, and it can often be repeated while changing the DB schema.

像我们在上一篇文章中创建的那些简单的固定装置非常适合开发阶段,在该阶段可以快速完成约30个实体的加载,并且在更改数据库架构时通常可以重复执行。

Testing app performance, simulating real-world traffic and detecting bottlenecks requires bigger datasets (i.e. a larger amount of database entries and image files for this project). Generating thousands of entries takes some time (and computer resources), so we want to do it only once.

测试应用程序性能,模拟实际流量并检测瓶颈需要更大的数据集(例如,该项目需要更多的数据库条目和图像文件)。 生成数千个条目需要一些时间(和计算机资源),因此我们只想执行一次。

We could try increasing the COUNT constant in our fixture classes and seeing what will happen:

我们可以尝试在灯具类中增加COUNT常数,看看会发生什么:

// src/DataFixtures/ORM/LoadUsersData.php
class LoadUsersData extends AbstractFixture implements ContainerAwareInterface, OrderedFixtureInterface
{
    const COUNT = 500;
    ...
}

// src/DataFixtures/ORM/LoadGalleriesData.php
class LoadGalleriesData extends AbstractFixture implements ContainerAwareInterface, OrderedFixtureInterface
{
    const COUNT = 1000;
    ...
}

Now, if we run bin/refreshDb.sh, after some time we’ll probably get a not-so-nice message like PHP Fatal error: Allowed memory size of N bytes exhausted.

现在,如果我们运行bin / refreshDb.sh ,一段时间之后,我们可能会收到不太好的消息,例如PHP Fatal error: Allowed memory size of N bytes exhausted

Apart from slow execution, every error would result in an empty database because EntityManager is flushed only at the very end of the fixture class. Additionally, Faker is downloading a random image for every gallery entry. For 1,000 galleries with 5 to 10 images per gallery that would be 5,000 – 10,000 downloads, which is really slow.

除了执行缓慢之外,每个错误都将导致数据库为空,因为EntityManager仅在Fixture类的最后才刷新。 此外,Faker正在为每个图库条目下载一个随机图像。 对于1,000个画廊,每个画廊有5至10张图像,这将是5,000 – 10,000次下载,这确实很慢。

There are excellent resources on optimizing Doctrine and Symfony for batch processing, and we’re going to use some of these tips to optimize fixtures loading.

关于为批量处理优化DoctrineSymfony的资源非常丰富,我们将使用其中的一些技巧来优化灯具的加载。

First, we’ll define a batch size of 100 galleries. After every batch, we’ll flush and clear the EntityManager (i.e., detach persisted entities) and tell the garbage collector to do its job.

首先,我们将定义100个画廊的批次大小。 每批处理之后,我们将刷新并清除EntityManager (即,分离持久性实体),并告诉垃圾收集器完成其工作。

To track progress, let’s print out some meta information (batch identifier and memory usage).

为了跟踪进度,让我们打印一些元信息(批处理标识符和内存使用情况)。

Note: After calling $manager->clear(), all persisted entities are now unmanaged. The entity manager doesn’t know about them anymore, and you’ll probably get an “entity-not-persisted” error.

注意:在调用$manager->clear() ,所有持久化的实体现在都不$manager->clear()管理。 实体经理不再了解它们,您可能会收到“实体不持久”的错误。

The key is to merge the entity back to the manager $entity = $manager->merge($entity);

关键是将实体合并回管理器。 $entity = $manager->merge($entity);

Without the optimization, memory usage is increasing while running a LoadGalleriesData fixture class:

如果不进行优化,则在运行LoadGalleriesData夹具类时,内存使用量会增加:

> loading [200] App\DataFixtures\ORM\LoadGalleriesData
100 Memory usage (currently) 24MB / (max) 24MB
200 Memory usage (currently) 26MB / (max) 26MB
300 Memory usage (currently) 28MB / (max) 28MB
400 Memory usage (currently) 30MB / (max) 30MB
500 Memory usage (currently) 32MB / (max) 32MB
600 Memory usage (currently) 34MB / (max) 34MB
700 Memory usage (currently) 36MB / (max) 36MB
800 Memory usage (currently) 38MB / (max) 38MB
900 Memory usage (currently) 40MB / (max) 40MB
1000 Memory usage (currently) 42MB / (max) 42MB

Memory usage starts at 24 MB and increases for 2 MB for every batch (100 galleries). If we tried to load 100,000 galleries, we’d need 24 MB + 999 (999 batches of 100 galleries, 99,900 galleries) * 2 MB = ~2 GB of memory.

内存使用量从24 MB开始,每批次(100个图库)增加2 MB。 如果我们尝试加载100,000个库,则需要24 MB + 999(999个批次的100个库,99,900个库)* 2 MB =〜2 GB的内存

After adding $manager->flush() and gc_collect_cycles() for every batch, removing SQL logging with $manager->getConnection()->getConfiguration()->setSQLLogger(null) and removing entity references by commenting out $this->addReference('gallery' . $i, $gallery);, memory usage becomes somewhat constant for every batch.

为每个批次添加$manager->flush()gc_collect_cycles() ,使用$manager->getConnection()->getConfiguration()->setSQLLogger(null)删除SQL日志记录,并通过注释掉$this->addReference('gallery' . $i, $gallery);删除实体引用$this->addReference('gallery' . $i, $gallery); ,每个批次的内存使用量都会变得恒定。

// Define batch size outside of the for loop
$batchSize = 100;

...

for ($i = 1; $i <= self::COUNT; $i++) {
    ...

    // Save the batch at the end of the for loop
    if (($i % $batchSize) == 0 || $i == self::COUNT) {
        $currentMemoryUsage = round(memory_get_usage(true) / 1024);
        $maxMemoryUsage = round(memory_get_peak_usage(true) / 1024);
        echo sprintf("%s Memory usage (currently) %dKB/ (max) %dKB \n", $i, $currentMemoryUsage, $maxMemoryUsage);

        $manager->flush();
        $manager->clear();

        // here you should merge entities you're re-using with the $manager
        // because they aren't managed anymore after calling $manager->clear();
        // e.g. if you've already loaded category or tag entities
        // $category = $manager->merge($category);

        gc_collect_cycles();
    }
}

As expected, memory usage is now stable:

如预期的那样,内存使用情况现已稳定:

> loading [200] App\DataFixtures\ORM\LoadGalleriesData
100 Memory usage (currently) 24MB / (max) 24MB
200 Memory usage (currently) 26MB / (max) 28MB
300 Memory usage (currently) 26MB / (max) 28MB
400 Memory usage (currently) 26MB / (max) 28MB
500 Memory usage (currently) 26MB / (max) 28MB
600 Memory usage (currently) 26MB / (max) 28MB
700 Memory usage (currently) 26MB / (max) 28MB
800 Memory usage (currently) 26MB / (max) 28MB
900 Memory usage (currently) 26MB / (max) 28MB
1000 Memory usage (currently) 26MB / (max) 28MB

Instead of downloading random images every time, we can prepare 15 random images and update the fixture script to randomly choose one of them instead of using Faker’s $faker->image() method.

无需每次都下载随机图像,我们可以准备15张随机图像并更新$faker->image()脚本以随机选择其中之一,而不是使用Faker的$faker->image()方法。

Let’s take 15 images from Unsplash and save them in var/demo-data/sample-images.

让我们从Unsplash中获取15张图像,并将其保存在var/demo-data/sample-images

Then, update the LoadGalleriesData::generateRandomImage method:

然后,更新LoadGalleriesData::generateRandomImage方法:

private function generateRandomImage($imageName)
    {
        $images = [
            'image1.jpeg',
            'image10.jpeg',
            'image11.jpeg',
            'image12.jpg',
            'image13.jpeg',
            'image14.jpeg',
            'image15.jpeg',
            'image2.jpeg',
            'image3.jpeg',
            'image4.jpeg',
            'image5.jpeg',
            'image6.jpeg',
            'image7.jpeg',
            'image8.jpeg',
            'image9.jpeg',
        ];

        $sourceDirectory = $this->container->getParameter('kernel.project_dir') . '/var/demo-data/sample-images/';
        $targetDirectory = $this->container->getParameter('kernel.project_dir') . '/var/uploads/';

        $randomImage = $images[rand(0, count($images) - 1)];
        $randomImageSourceFilePath = $sourceDirectory . $randomImage;
        $randomImageExtension = explode('.', $randomImage)[1];
        $targetImageFilename = sha1(microtime() . rand()) . '.' . $randomImageExtension;
        copy($randomImageSourceFilePath, $targetDirectory . $targetImageFilename);

        $image = new Image(
            Uuid::getFactory()->uuid4(),
            $randomImage,
            $targetImageFilename
        );

        return $image;
    }

It’s a good idea to remove old files in var/uploads when reloading fixtures, so I’m adding rm var/uploads/* command to bin/refreshDb.sh script, immediately after dropping the DB schema.

重新加载固定装置时,最好删除var/uploads中的旧文件,因此,我在删除数据库模式后立即将rm var/uploads/*命令添加到bin/refreshDb.sh脚本中。

Loading 500 users and 1000 galleries now takes ~7 minutes and ~28 MB of memory (peak usage).

现在,加载500个用户和1000个画廊需要大约7分钟的时间和大约28 MB的内存(高峰使用)。

Dropping database schema...
Database schema dropped successfully!
ATTENTION: This operation should not be executed in a production environment.

Creating database schema...
Database schema created successfully!
  > purging database
  > loading [100] App\DataFixtures\ORM\LoadUsersData
300 Memory usage (currently) 10MB / (max) 10MB
500 Memory usage (currently) 12MB / (max) 12MB
  > loading [200] App\DataFixtures\ORM\LoadGalleriesData
100 Memory usage (currently) 24MB / (max) 26MB
200 Memory usage (currently) 26MB / (max) 28MB
300 Memory usage (currently) 26MB / (max) 28MB
400 Memory usage (currently) 26MB / (max) 28MB
500 Memory usage (currently) 26MB / (max) 28MB
600 Memory usage (currently) 26MB / (max) 28MB
700 Memory usage (currently) 26MB / (max) 28MB
800 Memory usage (currently) 26MB / (max) 28MB
900 Memory usage (currently) 26MB / (max) 28MB
1000 Memory usage (currently) 26MB / (max) 28MB

Take a look at the fixture classes source: LoadUsersData.php and LoadGalleriesData.php.

看一下灯具类的源代码: LoadUsersData.phpLoadGalleriesData.php

性能 (Performance)

At this point, the homepage rendering is very slow — way too slow for production.

这一点上 ,主页渲染非常慢-对于生产来说太慢了。

A user can feel that the app is struggling to deliver the page, probably because the app is rendering all the galleries instead of a limited number.

用户可能会觉得该应用程序在交付页面方面很麻烦,这可能是因为该应用程序正在渲染所有画廊而不是有限数量的画廊。

Instead of rendering all galleries at once, we could update the app to render only the first 12 galleries immediately and introduce lazy load. When the user scrolls to the end of the screen, the app will fetch next 12 galleries and present them to the user.

无需一次渲染所有图库,我们可以更新应用程序以立即仅渲染前12个画廊并引入延迟加载。 当用户滚动到屏幕末端时,该应用将获取接下来的12个画廊并将其展示给用户。

性能测试 (Performance tests)

To track performance optimization, we need to establish a fixed set of tests that will be used to test and benchmark performance improvements relatively.

为了跟踪性能优化,我们需要建立一组固定的测试,这些测试将用于相对地测试和基准化性能改进。

We will use Siege for load testing. Here you can find more about Siege and performance testing. Instead of installing Siege on my machine, we can utilize Docker — a powerful container platform.

我们将使用Siege进行负载测试。 在这里,您可以找到有关围攻和性能测试的更多信息。 不用在我的机器上安装Siege,我们可以利用Docker –一个强大的容器平台。

In simple terms, Docker containers are similar to virtual machines (but they aren’t the same thing). Except for building and deploying apps, Docker can be used to experiment with applications without actually installing them on your local machine. You can build your images or use images available on Docker Hub, a public registry of Docker images.

简而言之,Docker容器类似于虚拟机( 但它们不是同一件事 )。 除了构建和部署应用程序之外,Docker可以用于试验应用程序,而无需实际将其安装在本地计算机上。 您可以构建映像或使用Docker Hub (Docker映像的公共注册表)上可用的映像。

It’s especially useful when you want to experiment with different versions of the same software (for example, different versions of PHP).

当您要尝试使用同一软件的不同版本(例如,不同版本PHP)时,它特别有用。

We’ll use the yokogawa/siege image to test the app.

我们将使用横河/围攻图片来测试该应用。

测试首页 (Testing the home page)

Testing the home page is not trivial, since there are Ajax requests executed only when the user scrolls to the end of the page.

测试主页并非易事,因为只有当用户滚动到页面末尾时才会执行Ajax请求。

We could expect all users to land on the home page (i.e., 100%). We could also estimate that 50% of them would scroll down to the end and therefore request the second page of galleries. We could also guess that 30% of them would load the third page, 15% would request the fourth page, and 5% would request the fifth page.

我们可以期望所有用户都可以登陆首页(即100%)。 我们还可以估计其中的50%将向下滚动到末尾,因此请求画廊的第二页。 我们还可以猜测其中有30%会加载第三页,有15%会加载第四页,而有5%会加载第五页。

These numbers are based on predictions, and it would be much better if we could use an analytics tool to get an actual insight in users’ behavior. But that’s impossible for a brand new app. Still, it’s a good idea to take a look at analytics data now and then and adjust your test suite after the initial deploy.

这些数字是基于预测的,如果我们可以使用分析工具获得对用户行为的实际洞察力,那就更好了。 但这对于全新的应用程序是不可能的。 尽管如此,还是最好先查看一下分析数据,然后在初次部署后调整测试套件。

We’ll test the home page (and lazy load URLs) with two tests running in parallel. The first one will be testing the home page URL only, while another one will test lazy load endpoint URLs.

我们将通过两个并行运行的测试来测试主页(和延迟加载URL)。 第一个将仅测试主页URL,而另一个将测试延迟加载端点URL。

File lazy-load-urls.txt contains a randomized list of lazily loaded pages URLs in predicted ratios:

文件lazy-load-urls.txt包含按预期比例延迟加载的页面URL的随机列表:

  • 10 URLs for the second page (50%)

    第二页10个网址(50%)
  • 6 URLs for third page (30%)

    第三页的6个URL(30%)
  • 3 URLs for fourth page (15%)

    第四页的3个网址(15%)
  • 1 URLs for fifth page (5%)

    第五页1个网址(5%)
http://blog.app/galleries-lazy-load?page=2
http://blog.app/galleries-lazy-load?page=2
http://blog.app/galleries-lazy-load?page=2
http://blog.app/galleries-lazy-load?page=4
http://blog.app/galleries-lazy-load?page=2
http://blog.app/galleries-lazy-load?page=2
http://blog.app/galleries-lazy-load?page=3
http://blog.app/galleries-lazy-load?page=2
http://blog.app/galleries-lazy-load?page=2
http://blog.app/galleries-lazy-load?page=4
http://blog.app/galleries-lazy-load?page=2
http://blog.app/galleries-lazy-load?page=4
http://blog.app/galleries-lazy-load?page=2
http://blog.app/galleries-lazy-load?page=3
http://blog.app/galleries-lazy-load?page=3
http://blog.app/galleries-lazy-load?page=3
http://blog.app/galleries-lazy-load?page=5
http://blog.app/galleries-lazy-load?page=3
http://blog.app/galleries-lazy-load?page=2
http://blog.app/galleries-lazy-load?page=3

The script for testing homepage performance will run 2 Siege processes in parallel, one against home page and another one against a generated list of URLs.

测试首页性能的脚本将并行运行2个Siege进程,一个针对主页,另一个针对生成的URL列表。

To execute a single HTTP request with Siege (in Docker), run:

要使用Siege(在Docker中)执行单个HTTP请求,请运行:

docker run --rm -t yokogawa/siege -c1 -r1 blog.app

Note: if you aren’t using Docker, you can omit the docker run --rm -t yokogawa/siege part and run Siege with the same arguments.

注意:如果您不使用Docker,则可以省略docker run --rm -t yokogawa/siege部分,并使用相同的参数运行Siege。

To run a 1-minute test with 50 concurrent users against the home page with a 1-second delay, execute:

要针对50个并发用户在首页上进行1分钟的测试(延迟1秒),请执行以下操作:

docker run --rm -t yokogawa/siege -d1 -c50 -t1M http://blog.app

To run a 1-minute test with 50 concurrent users against URLs in lazy-load-urls.txt, execute:

要对50个并发用户针对lazy-load-urls.txt中的URL运行1分钟测试,请执行以下操作:

docker run --rm -v `pwd`:/var/siege:ro -t yokogawa/siege -i --file=/var/siege/lazy-load-urls.txt -d1 -c50 -t1M

Do this from the directory where your lazy-load-urls.txt is located (that directory will be mounted as a read-only volume in Docker).

从您的lazy-load-urls.txt所在的目录中执行此操作(该目录将在Docker中作为只读卷挂载)。

Running a script test-homepage.sh will start 2 Siege processes (in a way suggested by this Stack Overflow answer) and output results.

运行脚本test-homepage.sh将启动2个Siege进程(以此Stack Overflow答案所建议的方式)并输出结果。

Assume we’ve deployed the app on a server with Nginx and with PHP-FPM 7.1 and loaded 25,000 users and 30,000 galleries. The results from load testing the app home page are:

假设我们已将应用程序部署在具有Nginx和PHP-FPM 7.1的服务器上,并加载了25,000个用户和30,000个图库。 对应用程序主页进行负载测试的结果是:

./test-homepage.sh

Transactions:               499 hits
Availability:               100.00 %
Elapsed time:               59.10 secs
Data transferred:           1.49 MB
Response time:              4.75 secs
Transaction rate:           8.44 trans/sec
Throughput:                 0.03 MB/sec
Concurrency:                40.09
Successful transactions:    499
Failed transactions:        0
Longest transaction:        16.47
Shortest transaction:       0.17

Transactions:               482 hits
Availability:               100.00 %
Elapsed time:               59.08 secs
Data transferred:           6.01 MB
Response time:              4.72 secs
Transaction rate:           8.16 trans/sec
Throughput:                 0.10 MB/sec
Concurrency:                38.49
Successful transactions:    482
Failed transactions:        0
Longest transaction:        15.36
Shortest transaction:       0.15

Even though app availability is 100% for both home page and lazy-load tests, response time is ~5 seconds, which is not something we’d expect from a high-performance app.

即使主页和延迟加载测试的应用程序可用性均为100%,响应时间仍为〜5秒,这不是我们期望高性能应用程序获得的结果。

测试一个图库页面 (Testing a single gallery page)

Testing a single gallery page is a little bit simpler: we’ll run Siege against the galleries.txt file, where we have a list of single gallery page URLs to test.

测试单个画廊页面要简单一些:我们将对galleries.txt文件运行Siege,在该文件中,我们列出了要测试的单个画廊页面URL的列表。

From the directory where the galleries.txt file is located (that directory will be mounted as a read-only volume in Docker), run this command:

galleries.txt文件所在的目录(该目录将在Docker中作为只读卷挂载),运行以下命令:

docker run --rm -v `pwd`:/var/siege:ro -t yokogawa/siege -i --file=/var/siege/galleries.txt -d1 -c50 -t1M

Load test results for single gallery pages are somewhat better than for the home page:

单个图库页面的负载测试结果比主页要好一些:

./test-single-gallery.sh
** SIEGE 3.0.5
** Preparing 50 concurrent users for battle.
The server is now under siege...
Lifting the server siege...      done.

Transactions:               3589 hits
Availability:               100.00 %
Elapsed time:               59.64 secs
Data transferred:           11.15 MB
Response time:              0.33 secs
Transaction rate:           60.18 trans/sec
Throughput:                 0.19 MB/sec
Concurrency:                19.62
Successful transactions:    3589
Failed transactions:        0
Longest transaction:        1.25
Shortest transaction:       0.10

测试,测试,测试 (Tests, Tests, Tests)

To make sure we’re not breaking anything with improvements we implement in the future, we need at least some tests.

为了确保我们在将来实现的改进中不会破坏任何东西,我们至少需要一些测试。

First, we require PHPUnit as a dev dependency:

首先,我们需要将PHPUnit作为dev依赖项:

composer req --dev phpunit

Then we’ll create a simple PHPUnit configuration by copying phpunit.xml.dist created by Flex to phpunit.xml and update environment variables (e.g., DATABASE_URL variable for the test environment). Also, I’m adding phpunit.xml to .gitignore.

然后,我们将通过复制创建一个简单PHPUnit配置phpunit.xml.dist由Flex创建phpunit.xml和更新环境变量(例如, DATABASE_URL测试环境变量)。 另外,我将phpunit.xml添加到.gitignore

Next, we create basic functional/smoke tests for the blog home page and single gallery pages. Smoke testing is a “preliminary testing to reveal simple failures severe enough to reject a prospective software release”. Since it’s quite easy to implement smoke tests, there’s no valid reason why you should avoid them!

接下来,我们为博客主页和单个图库页面创建基本的功能/烟雾测试 。 冒烟测试是“初步测试,以显示严重到足以拒绝预期的软件版本的简单故障” 。 由于实施烟雾测试非常容易,因此没有合理的理由应该避免使用烟雾测试!

These tests would only assert that URLs you provide in the urlProvider() method are resulting in a successful HTTP response code (i.e., HTTP status code is 2xx or 3xx).

这些测试只会断言您在urlProvider()方法中提供的URL会导致成功的HTTP响应代码(即HTTP状态代码为2xx或3xx)。

Simple smoke testing the home page and five single gallery pages could look like this:

简单的烟雾测试主页和五个单独的图库页面可能看起来像这样

namespace App\Tests;

use App\Entity\Gallery;
use Psr\Container\ContainerInterface;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\Routing\RouterInterface;

class SmokeTest extends WebTestCase
{
    /** @var  ContainerInterface */
    private $container;

    /**
     * @dataProvider urlProvider
     */
    public function testPageIsSuccessful($url)
    {
        $client = self::createClient();
        $client->request('GET', $url);

        $this->assertTrue($client->getResponse()->isSuccessful());
    }

    public function urlProvider()
    {
        $client = self::createClient();
        $this->container = $client->getContainer();

        $urls = [
            ['/'],
        ];

        $urls += $this->getGalleriesUrls();

        return $urls;
    }

    private function getGalleriesUrls()
    {
        $router = $this->container->get('router');
        $doctrine = $this->container->get('doctrine');
        $galleries = $doctrine->getRepository(Gallery::class)->findBy([], null, 5);

        $urls = [];

        /** @var Gallery $gallery */
        foreach ($galleries as $gallery) {
            $urls[] = [
                '/' . $router->generate('gallery.single-gallery', ['id' => $gallery->getId()],
                    RouterInterface::RELATIVE_PATH),
            ];
        }

        return $urls;
    }

}

Run ./vendor/bin/phpunit and see if tests are passing:

运行./vendor/bin/phpunit并查看测试是否通过:

./vendor/bin/phpunit
PHPUnit 6.5-dev by Sebastian Bergmann and contributors.

...

5 / 5 (100%)

Time: 4.06 seconds, Memory: 16.00MB

OK (5 tests, 5 assertions)

Note that it’s better to hardcode important URLs (e.g., for static pages or some well-known URLs) than to generate them within the test. Learn more about PHPUnit and TDD here.

请注意,硬编码重要的URL(例如,用于静态页面或某些知名的URL)比在测试中生成它们要好。 在此处了解有关PHPUnit和TDD的更多信息。

敬请关注 (Stay Tuned)

Upcoming articles in this series will cover details about PHP and MySQL performance optimization, improving overall performance perception and other tips and tricks for better app performance.

本系列即将发表的文章将涵盖有关PHP和MySQL性能优化,改善整体性能感知以及其他技巧和窍门以提高应用程序性能的详细信息。

翻译自: https://www.sitepoint.com/building-image-gallery-blog-symfony-flex-data-testing/

symfony连接数据库

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值