Public speaking course notes Read "Dynamo, Amazon’s Highly Available Key-value Store" Read "Bigtable, A Distributed Storage System for Structured Data" Read "Streaming Systems" 3, Watermarks Read "Streaming Systems" 1&2, Streaming 101 Read "F1, a distributed SQL database that scales" Read "Zanzibar, Google’s Consistent, Global Authorization System" Read "Spanner, Google's Globally-Distributed Database" Read "Designing Data-intensive applications" 12, The Future of Data Systems IOS development with Swift Read "Designing Data-intensive applications" 10&11, Batch and Stream Processing Read "Designing Data-intensive applications" 9, Consistency and Consensus Read "Designing Data-intensive applications" 8, Distributed System Troubles Read "Designing Data-intensive applications" 7, Transactions Read "Designing Data-intensive applications" 6, Partitioning Read "Designing Data-intensive applications" 5, Replication Read "Designing Data-intensive applications" 3&4, Storage, Retrieval, Encoding Read "Designing Data-intensive applications" 1&2, Foundation of Data Systems Three cases of binary search TAMU Operating System 2 Memory Management TAMU Operating System 1 Introduction Overview in cloud computing 2 TAMU Operating System 7 Virtualization TAMU Operating System 6 File System TAMU Operating System 5 I/O and Disk Management TAMU Operating System 4 Synchronization TAMU Operating System 3 Concurrency and Threading TAMU Computer Networks 5 Data Link Layer TAMU Computer Networks 4 Network Layer TAMU Computer Networks 3 Transport Layer TAMU Computer Networks 2 Application Layer TAMU Computer Networks 1 Introduction Overview in distributed systems and cloud computing 1 A well-optimized Union-Find implementation, in Java A heap implementation supporting deletion TAMU Advanced Algorithms 3, Maximum Bandwidth Path (Dijkstra, MST, Linear) TAMU Advanced Algorithms 2, B+ tree and Segment Intersection TAMU Advanced Algorithms 1, BST, 2-3 Tree and Heap TAMU AI, Searching problems Factorization Machine and Field-aware Factorization Machine for CTR prediction TAMU Neural Network 10 Information-Theoretic Models TAMU Neural Network 9 Principal Component Analysis TAMU Neural Network 8 Neurodynamics TAMU Neural Network 7 Self-Organizing Maps TAMU Neural Network 6 Deep Learning Overview TAMU Neural Network 5 Radial-Basis Function Networks TAMU Neural Network 4 Multi-Layer Perceptrons TAMU Neural Network 3 Single-Layer Perceptrons Princeton Algorithms P1W6 Hash Tables & Symbol Table Applications Stanford ML 11 Application Example Photo OCR Stanford ML 10 Large Scale Machine Learning Stanford ML 9 Anomaly Detection and Recommender Systems Stanford ML 8 Clustering & Principal Component Analysis Princeton Algorithms P1W5 Balanced Search Trees TAMU Neural Network 2 Learning Processes TAMU Neural Network 1 Introduction Stanford ML 7 Support Vector Machine Stanford ML 6 Evaluate Algorithms Princeton Algorithms P1W4 Priority Queues and Symbol Tables Stanford ML 5 Neural Networks Learning Princeton Algorithms P1W3 Mergesort and Quicksort Stanford ML 4 Neural Networks Basics Princeton Algorithms P1W2 Stack and Queue, Basic Sorts Stanford ML 3 Classification Problems Stanford ML 2 Multivariate Regression and Normal Equation Princeton Algorithms P1W1 Union and Find Stanford ML 1 Introduction and Parameter Learning

Rspec rocks

2016-06-05

One: Case Study

Debug sucks, and test rocks! If you use rails, then you should know how to use RSpec to test your rails app.

I use a simple case to practice basic RSpec. If you want practice, go to check the saasbook/bdd-tdd-cycle practice at the end of this articel. Here is the case.

User
`name:string and email:string`
`name and email should not be nil.`

I will test the validation of model and basic controller actions. Some revelent good resources are list at the end of this post.

Two: Set Up

Gem

We will use the most popular gems combo here.

group :development, :test do
  gem 'rspec-rails' 
  gem 'factory_girl_rails' #give us some test data to play with
end
group :test do
  gem 'capybara' #simulate users’ actions
  #detects changes and run test automatically, time saver!
  #if you use mac, make sure `gem install rb-fsevent`
  gem 'guard-rspec'
  gem 'launchy' # open browser to help you debug
end

Generate

Then run bundle install to install these gems and run rails generate rspec:install to generate necessary files. Also, if you use capybara, make sure require "capybara/rspec" is included in your spec/spec_helper.rb file.

Configration

Usually, I don’t want the auto generated test file, so I would add these config to the config/application.rb file:

config.generators do |g|
    g.controller_specs false
    g.view_specs false
    g.helper_specs false
    g.routing_specs false
    g.request_specs false
end

Three: Factory Girl

It would be good if we have some data that is isolated from database to play with. And Factory Girl is a great tool to deal with this problem.

Here we want two users, one is valid and another is not. Create a new file named users.rb inside spec/factories/users.rb. And:

FactoryGirl.define do
	factory :user do |f|
		f.name "John Smith"
		f.email "hey@gmail.com"
	end

	factory :invalid_user, parent: :user do |f|
		# inherits from user
		f.name nil
	end
end

Four: Model

Now we can test our model easily. In this article, I just want to test validation of this simple User model. User with name and email are valid, invalid without name or email.

Create a new file called user_spec.rb(make sure ‘_spec’ is included to be detected by RSpec).

require 'rails_helper'
RSpec.describe UserModel, type: :model do
	describe User do
		it "has valid user" do
			# use create, just FactoryGirl(:user) is deprecated now
			expect(FactoryGirl.create(:user)).to be_valid
		end
		
		it "is invalid without a name" do
			# use build to skip validation, it will not call 'save' method
			# when use create, our test will always fail
			expect(FactoryGirl.bulid(:user, name: nil)).to_not be_valid
		end
		
		it "is invalid without email" do
			expect(FactoryGirl.bulid(:user, email: nil)).to_not be_valid
		end
	end
end

Now I think the minmum model test is done. Model is the most inportant part of rails app, make sure they are test with high coverge. You can use simplecov to generate report. Now let’s move on to controller test!

Five: Controller

In this short article, I will just test create, update and destroy, and skip index etc.

Create

describe "POST #create" do
	context "with valid attributes" do
		it "create a new user" do
			expect{
				post :create, user: FactoryGirl.attributes_for(:user)
			}.to change(User, :count).by(1)
		end

		it "redirect to new user" do
			post :create, user: FactoryGirl.attributes_for(:user)
			expect(response).to redirect_to(User.last)
			expect(flash[:notice]).to include('successfully')  
		end
	end

	context "with invalid attributes" do
		it "create a new user" do
			expect{
				post :create, user: FactoryGirl.attributes_for(:invalid_user)
			}.to_not change(User, :count)
		end

		it "render new template" do
			post :create, user: FactoryGirl.attributes_for(:invalid_user)
			expect(response).to render_template :new
		end
	end
end

Something to notice: user expect() instead should; organise your test with context if there are too many examples.

Delete

describe 'DELETE destroy' do
  before(:each) do
    # make our test DRY  
    @user = FactoryGirl.create(:user)
  end
  
  it "deletes the user" do
    # note syntax, http method and rails action
    expect{
      delete :destroy, id: @user        
    }.to change(User,:count).by(-1)
  end
    
  it "redirects to users#index" do
    delete :destroy, id: @user
    expect(response).to redirect_to users_url
  end
end

Update

describe 'PUT update' do
  before :each do
    @user = FactoryGirl.create(:user, name: "John", email: "hey@gmail.com")
  end
  
  context "valid update" do
    it "change user attributes" do
      put :update, id: @user, user: FactoryGirl.attributes_for(:user, name: "John", email: "hello@gmail.com")
      @user.reload
      expect(@user.name).to eq("John")
      expect(@user.email).to eq("hello@gmail.com")
    end
  
    it "redirects to the updated user" do
      put :update, id: @user, user: Factory.attributes_for(:user)
      expect(response).to redirect_to @user
    end
  end
  
  context "invalid update" do   
    it "does not update user's attributes" do
      put :update, id: @user, user: FactoryGirl.attributes_for(:user, name: "Michael", email: nil)
      @user.reload
      expect(@user.name).to_not eq("Michael")
      expect(@user.email).to eq("hey@gmail.com")
    end
    
    it "renders the edit view" do
      put :update, id: @user, user: FactoryGirl.attributes_for(:invalid_user)
      expect(response).to render_template :edit
    end
  end
end

Resources


Creative Commons License
Melon blog is created by melonskin. This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.
© 2016-2024. All rights reserved by melonskin. Powered by Jekyll.