Back to Repositories

Testing HTTP Method Defaults and Header Management in HTTPie CLI

This test suite validates HTTP method defaults and header handling in HTTPie CLI, focusing on content type negotiation and automatic header assignment. It ensures proper behavior for different request types and data formats.

Test Coverage Overview

The test suite provides comprehensive coverage of HTTPie’s default behaviors and header management.

  • Tests implicit HTTP methods (GET, POST) with various data formats
  • Validates automatic content-type and accept headers
  • Verifies form data vs JSON payload handling
  • Tests header case sensitivity and overrides

Implementation Analysis

The implementation uses pytest fixtures and mock environments to simulate different HTTP scenarios.

Key patterns include:
  • Mock environment creation for stdin/stdout testing
  • Httpbin integration for request validation
  • Structured test classes for related test cases
  • JSON response parsing and assertion checks

Technical Details

Testing infrastructure includes:

  • pytest as the testing framework
  • MockEnvironment for IO simulation
  • BytesIO for file handling
  • Httpbin for HTTP request validation
  • Custom utility functions for HTTP status verification

Best Practices Demonstrated

The test suite exemplifies several testing best practices:

  • Isolation of test cases using fixtures
  • Comprehensive edge case coverage
  • Clear test method naming conventions
  • Proper separation of concerns between test classes
  • Effective use of mock objects for environment control

httpie/cli

tests/test_defaults.py

            
"""
Tests for the provided defaults regarding HTTP method, and --json vs. --form.

"""
from io import BytesIO

from httpie.client import JSON_ACCEPT
from .utils import MockEnvironment, http, HTTP_OK
from .fixtures import FILE_PATH


def test_default_headers_case_insensitive(httpbin):
    """
    <https://github.com/httpie/cli/issues/644>
    """
    r = http(
        '--debug',
        '--print=H',
        httpbin + '/post',
        'CONTENT-TYPE:application/json-patch+json',
        'a=b',
    )
    assert 'CONTENT-TYPE: application/json-patch+json' in r
    assert 'Content-Type' not in r


# noinspection PyPep8Naming
class TestImplicitHTTPMethod:
    def test_implicit_GET(self, httpbin):
        r = http(httpbin + '/get')
        assert HTTP_OK in r

    def test_implicit_GET_with_headers(self, httpbin):
        r = http(httpbin + '/headers', 'Foo:bar')
        assert HTTP_OK in r
        assert r.json['headers']['Foo'] == 'bar'

    def test_implicit_POST_json(self, httpbin):
        r = http(httpbin + '/post', 'hello=world')
        assert HTTP_OK in r
        assert r.json['json'] == {'hello': 'world'}

    def test_implicit_POST_form(self, httpbin):
        r = http('--form', httpbin + '/post', 'foo=bar')
        assert HTTP_OK in r
        assert r.json['form'] == {'foo': 'bar'}

    def test_implicit_POST_raw(self, httpbin):
        r = http('--raw', 'foo bar', httpbin + '/post')
        assert HTTP_OK in r
        assert r.json['data'] == 'foo bar'

    def test_implicit_POST_stdin(self, httpbin):
        env = MockEnvironment(
            stdin_isatty=False,
            stdin=BytesIO(FILE_PATH.read_bytes())
        )
        r = http('--form', httpbin + '/post', env=env)
        assert HTTP_OK in r


class TestAutoContentTypeAndAcceptHeaders:
    """
    Test that `Accept` and `Content-Type` correctly default to JSON,
    but can still be overridden. The same with Content-Type when `--form`
    `-f` is used.

    """

    def test_GET_no_data_no_auto_headers(self, httpbin):
        # https://github.com/httpie/cli/issues/62
        r = http('GET', httpbin + '/headers')
        assert HTTP_OK in r
        assert r.json['headers']['Accept'] == '*/*'
        assert 'Content-Type' not in r.json['headers']

    def test_POST_no_data_no_auto_headers(self, httpbin):
        # JSON headers shouldn't be automatically set for POST with no data.
        r = http('POST', httpbin + '/post')
        assert HTTP_OK in r
        assert '"Accept": "*/*"' in r
        assert '"Content-Type": "application/json' not in r

    def test_POST_with_data_auto_JSON_headers(self, httpbin):
        r = http('POST', httpbin + '/post', 'a=b')
        assert HTTP_OK in r
        assert r.json['headers']['Accept'] == JSON_ACCEPT
        assert r.json['headers']['Content-Type'] == 'application/json'

    def test_GET_with_data_auto_JSON_headers(self, httpbin):
        # JSON headers should automatically be set also for GET with data.
        r = http('POST', httpbin + '/post', 'a=b')
        assert HTTP_OK in r
        assert r.json['headers']['Accept'] == JSON_ACCEPT
        assert r.json['headers']['Content-Type'] == 'application/json'

    def test_POST_explicit_JSON_JSON_ACCEPT(self, httpbin):
        r = http('--json', 'POST', httpbin + '/post')
        assert HTTP_OK in r
        assert r.json['headers']['Accept'] == JSON_ACCEPT
        # Make sure Content-Type gets set even with no data.
        # https://github.com/httpie/cli/issues/137
        assert 'application/json' in r.json['headers']['Content-Type']

    def test_GET_explicit_JSON_explicit_headers(self, httpbin):
        r = http('--json', 'GET', httpbin + '/headers',
                 'Accept:application/xml',
                 'Content-Type:application/xml')
        assert HTTP_OK in r
        assert '"Accept": "application/xml"' in r
        assert '"Content-Type": "application/xml"' in r

    def test_POST_form_auto_Content_Type(self, httpbin):
        r = http('--form', 'POST', httpbin + '/post')
        assert HTTP_OK in r
        assert '"Content-Type": "application/x-www-form-urlencoded' in r

    def test_POST_form_Content_Type_override(self, httpbin):
        r = http('--form', 'POST', httpbin + '/post',
                 'Content-Type:application/xml')
        assert HTTP_OK in r
        assert '"Content-Type": "application/xml"' in r

    def test_print_only_body_when_stdout_redirected_by_default(self, httpbin):
        env = MockEnvironment(stdin_isatty=True, stdout_isatty=False)
        r = http('GET', httpbin + '/get', env=env)
        assert 'HTTP/' not in r

    def test_print_overridable_when_stdout_redirected(self, httpbin):
        env = MockEnvironment(stdin_isatty=True, stdout_isatty=False)
        r = http('--print=h', 'GET', httpbin + '/get', env=env)
        assert HTTP_OK in r