[RUBY] Comment gérer l'événement où Committee :: InvalidRequest se produit en comité pendant le test de téléchargement de fichier Rspec

L'erreur qui se produit

     Committee::InvalidRequest:
       #/paths/~1contracts/post/requestBody/content/multipart~1form-data/schema/properties/original_file expected string, but received ActionDispatch::Http::UploadedFile: #<ActionDispatch::Http::UploadedFile:0x00007fa77e1f36a0>

Ce que je veux faire

Il définit une API qui reçoit les téléchargements de fichiers au format multipart / form-data. L'API est conçue sur la base des spécifications d'OpenAPI 3.0, par exemple:

requestBody:
  original_file:
    image/png:
      schema:
        type: string
        format: binary

Il est également dans la documentation de Swagger.

Je veux écrire une Rspec pour cette API en utilisant la gemme comité.

Tester le contenu

some_controller_spec.rb


#paramètres de formulaire
let(:file_upload_form) {
  {
    article_id: 1_000,
    name: 'This is good article',
    # set real existing file
    original_file: fixture_file_upload(
      Rails.root.join('sample_files/sample.docx'),
      'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
    )
  }
}

Les paramètres utilisés comme Request sont définis comme ci-dessus, et le fichier à télécharger est le fichier Docx préparé pour les tests. Ceci est POSTÉ comme suit et le format de l'API est vérifié par la méthode ʻassert_schema_conform`.

some_controller_spec.rb


it 'uploads file' do
  post api_v1_images_path,
         headers: authenticated_header(user),
         params: file_upload_form

  expect(response).to have_http_status(:success)
  assert_schema_conform
end

Cause d'erreur

La spécification de la requête API est «type: string», mais dans le test, l'objet de «ActionDispatch» est défini, donc j'obtiens une erreur indiquant que ce n'est pas ce à quoi je m'attendais. Cependant, puisqu'il s'agit d'un test de téléchargement de fichier, le traitement interne ne fonctionne pas avec String, il ne peut donc pas être modifié. C'était un problème.

Situation

Où l'erreur s'est produite

L'erreur se produit dans le processus ʻOpenAPIParser` suivant. [lib/openapi_parser/schema_validators/string_validator.rb#L11] (https://github.com/ota42y/openapi_parser/blob/44c640cc103bbbb9e8029e41a8889e8fd9350902/lib/openapi_parser/schema_validators/string_validator.rb#L11)

lib/openapi_parser/schema_validators/string_validator.rb



    def coerce_and_validate(value, schema, **_keyword_args)
      return OpenAPIParser::ValidateError.build_error_result(value, schema) unless value.kind_of?(String)

      # ...Omis ci-dessous...

    end

Il regarde simplement si «value» est une classe «String», et si ce n'est pas «String», c'est une erreur. Cette fois, puisqu'il s'agit d'un objet de ʻActionDispatch, value.kind_of? (String) ʻest faux et une erreur se produit.

Même s'il s'agit de "type: string", s'il s'agit de "format: binary", il autorise d'autres classes que la classe "String".

Dans le cas de format: binay comme un fichier, vous pouvez changer les conditions afin de ne pas déclencher l'erreur. Créez un patch pour cela. Créez un module arbitraire qui définit la méthode correspondante coerce_and_validate (value, schema, ** _keyword_args) comme un lot.

module StringValidatorPatch
  def coerce_and_validate(value, schema, **keyword_args)
    #Changer ce processus
    # https://github.com/ota42y/openapi_parser/blob/61874f0190a86c09bdfb78de5f51cfb6ae16068b/lib/openapi_parser/schema_validators/string_validator.rb#L11
    if !value.is_a?(String) && schema.format != 'binary'
      return OpenAPIParser::ValidateError.build_error_result(value, schema)
    end
    # ---Jusque là

    value, err = check_enum_include(value, schema)
    return [nil, err] if err

    value, err = pattern_validate(value, schema)
    return [nil, err] if err

    unless @datetime_coerce_class.nil?
      value, err = coerce_date_time(value, schema)
      return [nil, err] if err
    end

    value, err = validate_max_min_length(value, schema)
    return [nil, err] if err

    value, err = validate_email_format(value, schema)
    return [nil, err] if err

    value, err = validate_uuid_format(value, schema)
    return [nil, err] if err

    [value, nil]
  end
end

De cette manière, définissez un module qui redéfinit uniquement la méthode que vous souhaitez modifier. Rouvrez la classe à laquelle vous voulez patcher ce module, cette fois la classe ʻOpenAPIParser :: SchemaValidator :: StringValidator, et utilisez Module # prepend` pour écraser la méthode. Module # prepend reference

class OpenAPIParser::SchemaValidator::StringValidator
  prepend StringValidatorPatch
end

Cela éliminera l'erreur Committee :: InvalidRequest

Minimisez l'impact

Vous pouvez éviter l'erreur Committee :: InvalidRequest en appliquant un correctif, mais l'application d'un correctif dans une portée globale affectera le tout. Je souhaite que cette modification ne prenne effet que pour les tests impliquant des téléchargements de fichiers. Par conséquent, envisagez de le définir dans un "contexte" dans un contexte "do ... end" afin qu'il ne soit reflété que dans le contexte requis de Rspec.

some_controller_spec.rb


RSpec.describe SomeController, type: :request do
  context 'some context' do

    #Appliquer les correctifs en contexte
    module StringValidatorPatch
      def coerce_and_validate(value, schema, **keyword_args)
        if !value.is_a?(String) && schema.format != 'binary'
          return OpenAPIParser::ValidateError.build_error_result(value, schema)
        end
        # (Ce qui suit est omis)
      end
    end

    class OpenAPIParser::SchemaValidator::StringValidator
      prepend StringValidatorPatch
    end
    #Patch jusqu'à présent

    it 'uploads file' do
      post api_v1_images_path,
             headers: authenticated_header(user),
             params: file_upload_form

      expect(response).to have_http_status(:success)
      assert_schema_conform
    end
  end
end

Cependant, étant donné que la portée du bloc ne sépare pas les constantes ni ne définit les espaces de noms, nous voulons éviter un traitement tel que la définition de classe à l'intérieur du bloc. [Lint / ConstantDefinitionInBlock] de Rubocop (https://docs.rubocop.org/rubocop/cops_lint.html#lintconstantDefinitioninblock) est également bloqué. Si vous voulez définir une constante similaire dans Rspec, utilisez stub_const () pour la définir.

Utilisez stub_const () pour patcher là où vous en avez besoin

Utilisez Class.new pour rouvrir une classe sans le mot-clé class .. Vous pouvez également définir une classe en passant un bloc

Foo = Class.new {|c|
  def hello; 'hello'; end
}

puts Foo.new.hello # => 'hello'

Utilisez ceci pour définir une classe corrigée. Enregistrez le module StringValidatorPatch pour le correctif dans le répertoire spec / support sous le nom string_validator_patch.rb afin qu'il puisse être chargé.

patched = Class.new(OpenAPIParser::SchemaValidator::StringValidator) do |klass|
  klass.prepend StringValidatorPatch
end

stub_const('OpenAPIParser::SchemaValidator::StringValidator', patched)

Si vous passez une classe à l'argument de Class.new (), elle sera traitée comme une classe parente, donc dans le cas ci-dessus, patched sera une classe enfant de ʻOpenAPIParser :: SchemaValidator :: StringValidator. Si vous définissez une constante en utilisant stub_const ()`, vous pouvez utiliser la classe corrigée.

Implémentez ce processus avec before ou let selon les besoins.

Résumé

--Patch la classe Validator pour effacer l'erreur Committee :: InvalidRequest --Pour appliquer un patch, définissez un module qui implémente uniquement la méthode et utilisez prepend pour le refléter. --RSpec utilise Class.new () et stub_const () pour patcher localement

spec/support/string_validator_patch.rb


module StringValidatorPatch
  def coerce_and_validate(value, schema, **keyword_args)
    #Changer ce processus
    # https://github.com/ota42y/openapi_parser/blob/61874f0190a86c09bdfb78de5f51cfb6ae16068b/lib/openapi_parser/schema_validators/string_validator.rb#L11
    if !value.is_a?(String) && schema.format != 'binary'
      return OpenAPIParser::ValidateError.build_error_result(value, schema)
    end
    # ---Jusque là

    value, err = check_enum_include(value, schema)
    return [nil, err] if err

    value, err = pattern_validate(value, schema)
    return [nil, err] if err

    unless @datetime_coerce_class.nil?
      value, err = coerce_date_time(value, schema)
      return [nil, err] if err
    end

    value, err = validate_max_min_length(value, schema)
    return [nil, err] if err

    value, err = validate_email_format(value, schema)
    return [nil, err] if err

    value, err = validate_uuid_format(value, schema)
    return [nil, err] if err

    [value, nil]
  end
end

some_controller_spec.rb


RSpec.describe SomeController, type: :request do
  context 'some context' do
    let(:file_upload_form) {
      {
        article_id: 1_000,
        name: 'This is good article',
        original_file: fixture_file_upload(
          Rails.root.join('sample_files/sample.docx'),
          'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
        )
      }
    }

    before do
      #Appliquer les correctifs en contexte
      patched = Class.new(OpenAPIParser::SchemaValidator::StringValidator) do |klass|
        klass.prepend StringValidatorPatch
      end
      stub_const('OpenAPIParser::SchemaValidator::StringValidator', patched)
    end

    it 'uploads file' do
      post api_v1_images_path,
           headers: authenticated_header(user),
           params: file_upload_form

      expect(response).to have_http_status(:success)
      assert_schema_conform
    end
  end
end

Recommended Posts

Comment gérer l'événement où Committee :: InvalidRequest se produit en comité pendant le test de téléchargement de fichier Rspec
Comment tester l'écran de téléchargement de fichiers avec Spring + Selenium
Comment réaliser le téléchargement de fichiers avec Feign
Comment tester les interruptions pendant Thread.sleep avec JUnit
Comment résoudre les erreurs qui se produisent lors du test d'intégration "Ruby on Rails"
Comment enregistrer des fichiers avec l'extension spécifiée sous le répertoire spécifié en Java dans la liste
Comment déboguer le fichier jar généré avec Eclipse
Comment gérer l'erreur yaml.scanner.ScannerError: lors de la recherche du jeton suivant apparu lors de la création d'un environnement Rails avec Docker
Comment effacer l'image de test après avoir exécuté le test Rspec à l'aide de CarrierWave
Comment gérer le type auquel j'ai pensé en écrivant un programme Java pendant 2 ans
[Rails / RSpec] Comment traiter l'élément a une erreur de taille nulle
Comment créer un fichier jar sans dépendances dans Maven
Comment réaliser un téléchargement de fichiers volumineux avec TERASOLUNA 5.x (= Spring MVC)
Comment obtenir la longueur d'un fichier audio avec Java
Comment réaliser un téléchargement de fichiers volumineux avec Rest Template of Spring
Comment interagir avec un serveur qui ne plante pas l'application
Comment tester une méthode privée et la simuler partiellement en Java
[Rails] Comment obtenir les informations sur l'utilisateur actuellement connecté avec devise
[Rails] Comment appliquer le CSS utilisé dans l'application principale avec Administrer
Comment démarrer un conteneur Docker avec un volume monté dans un fichier de commandes
[Java] Comment utiliser la classe File
Comment filtrer JUnit Test dans Gradle
[Note] Comment démarrer avec Rspec
Comment ajouter un fichier jar dans ScalaIDE
Comment réaliser le téléchargement de fichiers avec Feign
Comment tester l'étendue privée avec JUnit
Comment gérer les actifs de précompilation a échoué.
[Rails] Comment lire le fichier XML téléchargé depuis l'écran en type Hash
Comment obtenir l'ID d'un utilisateur qui s'est authentifié avec Firebase dans Swift
[Rails] Comment enregistrer plusieurs enregistrements dans la table intermédiaire avec une association plusieurs-à-plusieurs
[Rails] Comment utiliser la méthode d'assistance utilisée dans l'application principale avec Administrer
Comment définir quand "Le constructeur Empty () n'est pas visible" se produit dans junit
Comment définir des variables d'environnement dans le fichier de propriétés de l'application Spring Boot
Comment tester une classe qui gère application.properties avec SpringBoot (requête: signalée)