Deep-Codes / cafeman

🍔 Cafeteria Management System with Ruby on Rails, PostgreSQL
https://cafeman-beta-test.herokuapp.com/
0 stars 2 forks source link

Add tests to cafeman #9

Open jcsherin opened 3 years ago

jcsherin commented 3 years ago

For a deployed application every modification introduces an element of risk. It could introduce unexpected behaviour. An existing feature could stop working. This is difficult to notice without tests. You may only find out when a user reports the bug in a live application.

You now have a working & live implementation of cafeman. This is a great point to start adding automated tests.

Rails has an excellent guide to get you started on testing here: https://guides.rubyonrails.org/testing.html You should add the following tests:

  1. model tests,
  2. integration tests and,
  3. functional tests for your controller.

Note: Please ignore system testing in the documentation. You do not have to add these.

Since this is a new topic, feel free to ask lots of questions and discuss it 😃

Deep-Codes commented 3 years ago

For Model, I have written some test :

class UserTest < ActiveSupport::TestCase
  test "valid user" do
    user = User.new(name: "Deep", email: "deep@gmail.com")
    assert_not user.valid?
  end

  test "invalid without name" do
    user = User.new(email: "deep@gmail.com", password: "test", role: "customer")
    assert_not user.valid?, "user is valid without a name"
    assert_not_nil user.errors[:name], "no validation error for name present"
  end

  test "invalid without email" do
    user = User.new(name: "Deep", role: "owner", password: "test")
    assert_not user.valid?
    assert_not_nil user.errors[:email], "no validation error for email present"
  end

  test "invalid without role" do
    user = User.new(name: "Deep", email: "deep@gmail.com", password: "test")
    assert_not user.valid?
    assert_not_nil user.errors[:role], "no validation error for role present"
  end

  test "invalid without pasword" do
    user = User.new(name: "Deep", email: "deep@gmail.com", role: "owner")
    assert_not user.valid?
    assert_not_nil user.errors[:pasword], "no validation error for pasword present"
  end
end

and when i run it:

bin/rails test test/models/user_test.rb
Running via Spring preloader in process 9185
Run options: --seed 18223

# Running:

.....

Finished in 0.204587s, 24.4395 runs/s, 43.9911 assertions/s.
5 runs, 9 assertions, 0 failures, 0 errors, 0 skips

Are tests supposed to be like this?

jcsherin commented 3 years ago

You are definitely on the right track.

When you test user.valid? you are testing the ActiveRecord::Validations API. This is unnecessary because you can move forward with the assumption that it would be tested in that project. This can be generalized. You do not need to test any third-party library/framework API within your application.

You should write test for your own code like: https://github.com/Deep-Codes/cafeman/blob/7c4f91b4ba42bf6ca229aa21e0be9f2ddfb427a3/app/models/user.rb#L7-L9

Deep-Codes commented 3 years ago

Hey sorry I was out yesterday

I tried some stuff

This Registers a new User:

require "test_helper"

class RegisterUsers < ActionDispatch::IntegrationTest
  test "can register" do
    assert_difference("User.count") do
      post "/users/",
           params: { email: "test@email.com ", password: "test_password", role: "customer", name: "test_name" }
    end
    assert_response :redirect
    follow_redirect!
    assert_equal "/", path
  end
end

Results:

❯ bin/rails test test/integration/register_user_test.rb
Running via Spring preloader in process 33500
Run options: --seed 12040

# Running:

.

Finished in 0.407556s, 2.4537 runs/s, 7.3610 assertions/s.
1 runs, 3 assertions, 0 failures, 0 errors, 0 skips

But to run other test like place an order , add a menu_item i have to have user signed in 1st

So i created a helper file in helpers/helpers.rb

def signin
  post "/users/",
    params: { email: "test@gmail.com ", password: "test", role: "customer", name: "test_name" }
end

Also

def create_menu
  post "/menus/",
    params: { name: "test_menu" }
end

We also need ActiveMenu and add MenuItem

ActiveMenu.create(active_menu: Menu.last.id)
MenuItem.create(menu_id: Menu.last.id, name: "name", description: "description", price: 45)

For creating the Order another helper method

def create_order
  new_order = Order.create(user_id: User.first.id, delivered_at: "", order_status: "cart")
  new_order.id
end

and then a writing test for placing an order

require "test_helper"
require_relative "../helpers/helpers.rb"

class OrderItemsTest < ActionDispatch::IntegrationTest
  test "can add order_items" do
    signin()
    create_menu()
    ActiveMenu.create(active_menu: Menu.last.id)
    MenuItem.create(menu_id: Menu.last.id, name: "name", description: "description", price: 45)
    order_id = create_order()
    assert_difference("OrderItem.count") do
      post "/order_items/",
           params: {
             order_id: order_id,
             menu_item_id: MenuItem.last.id,
             menu_item_name: "Coffee",
             menu_item_price: 40,
             count: 1,
           }
    end
    assert_response :redirect
    follow_redirect!
    assert_equal "/orders", path
  end
end

Result

❯ bin/rails test test/integration/order_items_test.rb
Running via Spring preloader in process 33704
Run options: --seed 29784

# Running:

.

Finished in 0.990202s, 1.0099 runs/s, 3.0297 assertions/s.
1 runs, 3 assertions, 0 failures, 0 errors, 0 skips

Are tests similar to this?

jcsherin commented 3 years ago

Yes, they are and you've also touched a few issues which prevents you from writing tests which can be refactored similar to any other code we write.

  1. You can use fixtures for your testing data. See: https://guides.rubyonrails.org/testing.html#the-low-down-on-fixtures
  2. You can use setup and teardown to set the pre & post conditions for a test. So in setup you can sign-in and in teardown you can sign-out. For each test you need a blank slate. Otherwise it is possible that one of the previously run tests would put the data in a particular state, and subsequent tests could depend on that state instead of the one you expect.
Deep-Codes commented 3 years ago

I am trying to create login test

i created a users fixtures: users.yml

deep:
  name: Deepankar Bhade
  role: customer
  email: deepankar@gmail.com
  password_digest: deepankar

then a helper sigin()

def signin(user)
  post "/users/",
    params: { email: user.email, password: user.password_digest, role: user.role, name: user.name }
end

then a sessions_controller_test.rb

require "test_helper"
require_relative "../helpers/helpers.rb"

class SessionsControllerTest < ActionDispatch::IntegrationTest
  setup do
    # ? Register a new user
    @user = users(:deep)
    signin(users(:deep))
  end

  test "should login a registers user" do
    post "/signin/",
         params: { email: @user.email, password: @user.password_digest }
  end
end

I haven't added any assert statement here as the test gives an error

❯ rails test test/controllers/sessions_controller_test.rb
Running via Spring preloader in process 41724
Run options: --seed 36581

# Running:

E

Error:
SessionsControllerTest#test_should_login_a_registers_user:
BCrypt::Errors::InvalidHash: invalid hash
    app/controllers/sessions_controller.rb:9:in `create'
    test/controllers/sessions_controller_test.rb:12:in `block in <class:SessionsControllerTest>'

rails test test/controllers/sessions_controller_test.rb:11

Finished in 0.406695s, 2.4588 runs/s, 0.0000 assertions/s.
1 runs, 0 assertions, 0 failures, 1 errors, 0 skips

what exactly am I doing wrong? 😅

Deep-Codes commented 3 years ago

Would create a PR on tests today

jcsherin commented 3 years ago
deep:
  ...
  password_digest: deepankar

Error: SessionsControllerTest#test_should_login_a_registers_user: BCrypt::Errors::InvalidHash: invalid hash

It looks like the password_digest value in your fixture was rejected because its not a valid hash. You will need to pass a digest of the password deepankar.

Deep-Codes commented 3 years ago
deep:
  ...
  password_digest: deepankar

Error: SessionsControllerTest#test_should_login_a_registers_user: BCrypt::Errors::InvalidHash: invalid hash

It looks like the password_digest value in your fixture was rejected because its not a valid hash. You will need to pass a digest of the password deepankar.

I think this solves it :

password_digest: <%= BCrypt::Password.create('deepankar') %>
Deep-Codes commented 3 years ago

I tried some test

❯ rails test
Running via Spring preloader in process 54916
Run options: --seed 9308

# Running:

...........

Finished in 1.962253s, 5.6058 runs/s, 17.8366 assertions/s.
11 runs, 35 assertions, 0 failures, 0 errors, 0 skips

I would try to add a test to placing orders now :)