DEV Community

Discussion on: How do you like to unit test your code?

Collapse
 
bhserna profile image
Benito Serna • Edited

How do I like to unit test?

I like to write unit tests on a "use case" level...

Normally I divide my code in modules that expose some kind of business functions like...

module Restaurant
  def self.get_menu(items_store)
  end

  def self.create_empty_order(orders_store)
  end

  def self.get_order(id, orders_store)
  end

  def self.get_orders(orders_store)
  end

  def self.add_item_to_order(order_id, item_id, items_store, order_items_store)
  end

  def self.update_order_item_quantity(order_item_id, quantity, order_items_store)
  end
end

To implement the expected behavior I normally will need to create some "internal" modules and clases... I don't write tests for those internal modules and clases, I try to always test through the interface of these modules. And I use mocks or doubles for everything that is passed as argument.

For example...

module Restaurant
  RSpec.describe "Get menu" do
    def item_with(attrs)
      ItemRecord.new(attrs)
    end

    def store_with(records)
      ItemsStore.new(records)
    end

    def get_menu(store)
      Restaurant.get_menu(store)
    end

    it "returns all the stored items" do
      store = store_with([item_with(name: "D1"), item_with(name: "D2")])
      items = get_menu(store)
      expect(items.count).to eq 2
    end

    describe "returns each item" do
      it "with name, price and description" do
        store = store_with([
          item_with(
            name: "D1",
            price: 110,
            description: "D1 desc"
          )
        ])

        item = get_menu(store).first
        expect(item.name).to eq "D1"
        expect(item.price).to eq 110
        expect(item.description).to eq "D1 desc"
      end

      it "knowing when is a dish" do
        store = store_with([item_with(category: "dishes")])
        item = get_menu(store).first
        expect(item).to be_dish
      end

      it "knowing when is not a dish" do
        store = store_with([item_with(category: "beverages")])
        item = get_menu(store).first
        expect(item).not_to be_dish
      end

      it "knowing when is a beverage" do
        store = store_with([item_with(category: "beverages")])
        item = get_menu(store).first
        expect(item).to be_beverage
      end

      it "knowing when is not a beverage" do
        store = store_with([item_with(category: "dishes")])
        item = get_menu(store).first
        expect(item).not_to be_beverage
      end
    end
  end
end

or...

module Restaurant
  RSpec.describe "Add item to order" do
    def order_with(attrs)
      OrderRecord.new(attrs)
    end

    def item_with(attrs)
      ItemRecord.new(attrs)
    end

    def order_item_with(attrs)
      OrderItemRecord.new(attrs)
    end

    def items_store_with(records)
      ItemsStore.new(records)
    end

    def order_items_store_with(records)
      OrderItemsStore.new(records)
    end

    def add_item_to_order(order_id, item_id, items_store, order_items_store)
      Restaurant.add_item_to_order(order_id, item_id, items_store, order_items_store)
    end

    attr_reader :order, :item, :items_store

    before do
      @order = order_with(id: 1)
      @item = item_with(name: "D1", price: 110, description: "D1 desc")
      @items_store = items_store_with([item])
    end

    it "creates an order item record" do
      order_items_store = order_items_store_with([])

      expect(order_items_store).
        to receive(:create).
        with(order_id: order.id, item_id: item.id, quantity: 1, name: "D1", price: 110)

      add_item_to_order(order.id, item.id, items_store, order_items_store)
    end

    it "updates the order item record, when an item is added more than once" do
      order_item = order_item_with(id: 1234, order_id: order.id, item_id: item.id, quantity: 1, price: 110)
      order_items_store = order_items_store_with([order_item])

      expect(order_items_store).
        to receive(:update).
        with(order_item.id, quantity: 2)

      add_item_to_order(order.id, item.id, items_store, order_items_store)
    end
  end
end

What seems to be more valuable for me?

I think what I value most is...

  • Fast tests, to know that the system is working after every change
  • Be able to make changes fast
  • Have a place to clearly see the expected behavior
  • Be able to remove code without regrets