How to test file download in Behat

泪湿孤枕 提交于 2019-12-01 17:30:35
Ian Bytchek

Assuming you are using the Selenium driver you could "click" on the link and $this->getSession()->wait(30) until the download is finished and then check the Downloads folder for the file.

That would be the simplest solution. Alternatively you can use a proxy, like BrowserMob, to watch all requests and then verify the response code. But that would be a really painful path for that alone.

The simplest way to check that the file is downloaded would be to define another step with a basic assertion.

/**
 * @Then /^the file ".+" should be downloaded$/
 */
public function assertFileDownloaded($filename) 
{
    if (!file_exists('/download/dir/' . $filename)) {
        throw new Exception();
    }
}

This might be problematic in situations when you download a file with the same name and the browser saves it under a different name. As a solution you can add a @BeforeScenario hook to clear the list of the know files.

Another issue would be the download dir itself – it might be different for other users / machines. To fix that you could pass the download dir in your behat.yml as a argument to the context constructor, read the docs for that.

But the best approach would be to pass the configuration to the Selenium specifying the download dir to ensure it's always clear and you know exactly where to search. I am not certain how to do that, but from the quick googling it seems to be possible.

Checkout this blog: https://www.jverdeyen.be/php/behat-file-downloads/

The basic idea is to copy the current session and do the request with Guzzle. After that you can check the response any way you like.

class FeatureContext extends \Behat\Behat\Context\BehatContext {

   /**
    * @When /^I try to download "([^"]*)"$/
    */
    public function iTryToDownload($url)
    {
        $cookies = $this->getSession()->getDriver()->getWebDriverSession()->getCookie('PHPSESSID');
        $cookie = new \Guzzle\Plugin\Cookie\Cookie();
        $cookie->setName($cookies[0]['name']);
        $cookie->setValue($cookies[0]['value']);
        $cookie->setDomain($cookies[0]['domain']);

        $jar = new \Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar();
        $jar->add($cookie);

        $client = new \Guzzle\Http\Client($this->getSession()->getCurrentUrl());
        $client->addSubscriber(new \Guzzle\Plugin\Cookie\CookiePlugin($jar));

        $request = $client->get($url);
        $this->response = $request->send();
    }

    /**
    * @Then /^I should see response status code "([^"]*)"$/
    */
    public function iShouldSeeResponseStatusCode($statusCode)
    {
        $responseStatusCode = $this->response->getStatusCode();

        if (!$responseStatusCode == intval($statusCode)) {
            throw new \Exception(sprintf("Did not see response status code %s, but %s.", $statusCode, $responseStatusCode));
        }
    }

    /**
    * @Then /^I should see in the header "([^"]*)":"([^"]*)"$/
    */
    public function iShouldSeeInTheHeader($header, $value)
    {
        $headers = $this->response->getHeaders();
        if ($headers->get($header) != $value) {
            throw new \Exception(sprintf("Did not see %s with value %s.", $header, $value));
        }
    }
}

Little modified iTryToDownload() function with using all cookies:

public function iTryToDownload($link) {
$elt = $this->getSession()->getPage()->findLink($link);
if($elt) {
  $value = $elt->getAttribute('href');
  $driver = $this->getSession()->getDriver();
  if ($driver instanceof \Behat\Mink\Driver\Selenium2Driver) {
    $ds = $driver->getWebDriverSession();
    $cookies = $ds->getAllCookies();
  } else {
    throw new \InvalidArgumentException('Not Selenium2Driver');
  }

  $jar = new \Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar();
  for ($i = 0; $i < count($cookies); $i++) {
    $cookie = new \Guzzle\Plugin\Cookie\Cookie();
    $cookie->setName($cookies[$i]['name']);
    $cookie->setValue($cookies[$i]['value']);
    $cookie->setDomain($cookies[$i]['domain']);
    $jar->add($cookie);
  }
  $client = new \Guzzle\Http\Client($this->getSession()->getCurrentUrl());
  $client->addSubscriber(new \Guzzle\Plugin\Cookie\CookiePlugin($jar));

  $request = $client->get($value);
  $this->response = $request->send();
} else {
  throw new \InvalidArgumentException(sprintf('Could not evaluate: "%s"', $link));
}
}

In project we have problem that we have two servers: one with web drivers and browsers and second with selenium hub. As result we decide to use curl request for fetching headers. So I wrote function which would called in step definition. Below you can find a function which use a standard php functions: curl_init()

/**
 * @param $request_url
 * @param $userToken
 * @return bool
 * @throws Exception
 */
private function makeCurlRequestForDownloadCSV($request_url, $userToken)
{
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $request_url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

    $headers = [
        'Content-Type: application/json',
        "Authorization: Bearer {$userToken}"
    ];
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

    $output = curl_exec($ch);
    $info = curl_getinfo($ch);
    $output .= "\n" . curl_error($ch);
    curl_close($ch);

    if ($output === false || $info['http_code'] != 200 || $info['content_type'] != "text/csv; charset=UTF-8") {
        $output = "No cURL data returned for $request_url [" . $info['http_code'] . "]";
        throw new Exception($output);
    } else {
        return true;
    }
}

How you can see I have authorization by token. If you want to understand what headers you should use you should download file manual and look request and response in browser's tab network

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!