How to correctly make a Unit test for a method that is downloading a file from FTP Server using Laravel 10

254 views Asked by At

I'm attempting to write a unit test, preferably in PEST PHP, to verify the following method:

Data is retrieved from the 'producers' table in the database and fed into the configuration. This configuration includes FTP server access details.

The file is then downloaded and saved locally.

How do you test something like this? Ideally, I would create a producer factory and then check if the file has been downloaded. However, I would like to avoid downloading real data if possible.

This is my simplified Class:

class FtpConnection extends Connection
{
    /**
     * @throws Exception
     */
    public function download()
    {
        // get the content and make sure it's utf-8 encoded
        $file = $this->encode(
            $this->getFile()
        );

        // save content localy
        return Storage::disk($this->disk)
            ->put($this->fileName(), $file);

    }

    /**
     * @throws Exception
     */
    protected function getFile()
    {
        $config = $this->config();

        $storage = Storage::createFtpDriver($config);

        throw_if(!$storage->exists($this->producer->path), new Exception('File not found at path: ' . $this->producer->path));

        return $storage->get($this->producer->path);
    }

    protected function encode($file)
    {
        $currentEncoding = mb_detect_encoding($file, 'UTF-8, ISO-8859-1, GBK');

        if($currentEncoding != 'UTF-8') {
            return mb_convert_encoding($file, 'UTF-8', $currentEncoding);
        } else {
            return $file;
        }
    }

    protected function config(): array
    {
        return [
            'driver' => $this->producer->type,
            'host' => $this->producer->host,
            'port' => $this->producer->port,
            'username' => $this->producer->username,
            'password' => Crypt::decryptString($this->producer->password),
        ];
    }

}

calling the class:

$ftpConnection = new FtpConnection(Producer::find(1))
$file = $ftpConnection->download();
1

There are 1 answers

2
Greg Ostry On

This is my approach to pass this test green.

it('downloads a file from ftp server successfully', function() {
    Storage::fake('ftp');

    $producer = Producer::factory()->make([
        'producer_id' => 'exampleId',
        'file_extension' => 'csv',
        'type' => 'ftp',
        'host' => 'ftp.example.com',
        'port' => 21,
        'username' => 'user',
        'password' => Crypt::encryptString('password'),
    ]);

    (new \Tests\Mock\FakeFtpConnection($producer))->download();

    Storage::disk('ftp')->assertExists('exampleId.csv');
});

GregOstry OP Posted 49 minutes ago Copy it('downloads a file from ftp server successfully', function() { Storage::fake('ftp');

$producer = Producer::factory()->make([
    'producer_id' => 'exampleId',
    'file_extension' => 'csv',
    'type' => 'ftp',
    'host' => 'ftp.example.com',
    'port' => 21,
    'username' => 'user',
    'password' => Crypt::encryptString('password'),
]);

(new \Tests\Mock\FakeFtpConnection($producer))->download();

Storage::disk('ftp')->assertExists('exampleId.csv');

});

I have created a FakeFtpConnection class that extends the FtpConnection class and overrides the download method. Thus, my mock class looks like this:

class FakeFtpConnection extends FtpConnection
{
    public function download(): bool
    {
        Storage::fake('ftp')->put($this->fileName(), 'test');
//        UploadedFile::fake()->create($this->fileName());
        return true;
    }
}