Skip to content

Instantly share code, notes, and snippets.

@elissonmichael
Forked from rodrigomanhaes/carrinho.rb
Last active June 26, 2018 02:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save elissonmichael/211c2c4f480ea12d19aed68843ade173 to your computer and use it in GitHub Desktop.
Save elissonmichael/211c2c4f480ea12d19aed68843ade173 to your computer and use it in GitHub Desktop.
Carrinho de Compras com Mock
class Carrinho
attr_reader :pedidos
def initialize(args)
@pedido_class = args[:pedido_class]
@pedidos = []
end
def adicionar(produto)
@pedidos << @pedido_class.new(produto)
end
def produtos
pedidos.map(&:produto)
end
def alterar_quantidade(produto, nova_quantidade)
encontra_pedido_com(produto).alterar_quantidade(nova_quantidade)
end
def quantidade(produto)
encontra_pedido_com(produto).quantidade
end
def total
pedidos.sum(&:total)
end
private
def encontra_pedido_com(produto)
pedidos.detect{ |pedido| pedido.produto == produto }
end
end
require 'minitest/autorun'
require_relative 'carrinho'
describe Carrinho do
def setup
@carrinho = Carrinho.new(pedido_class: Minitest::Mock)
@produto_dummy = Object.new
@carrinho.adicionar(@produto_dummy)
@pedido_mock = @carrinho.pedidos.first
end
describe :produtos do
it "retorna os produtos adicionados" do
@pedido_mock.expect(:produto, @produto_dummy)
@carrinho.produtos.must_equal [@produto_dummy]
end
end
describe :alterar_quantidade do
it "envia uma mensagem de comando para pedido" do
@pedido_mock.expect(:produto, @produto_dummy)
@pedido_mock.expect(:alterar_quantidade, true, [3])
@carrinho.alterar_quantidade(@produto_dummy, 3)
@pedido_mock.verify
end
end
# Segundo Metz não precisamos testar mensagens de saída
# que sejam apenas consultas, esse teste poderia ser removido.
describe :total do
it "envia uma mensagem de consulta para pedido" do
@pedido_mock.expect(:total, 30)
@carrinho.total
@pedido_mock.verify
end
end
end
class Pedido
attr_reader :produto, :quantidade
def initialize(produto)
@produto = produto
@quantidade = 1
end
def alterar_quantidade(nova_quantidade)
@quantidade = nova_quantidade
end
def total
produto.preco * quantidade
end
end
require 'minitest/autorun'
require_relative 'pedido'
describe Pedido do
def setup
@produto_mock = MiniTest::Mock.new
@pedido = Pedido.new(@produto_mock)
end
it "permite alterar a quantidade" do
@pedido.alterar_quantidade(99)
@pedido.quantidade.must_equal 99
end
it "retorna o valor total" do
@produto_mock.expect(:preco, 30)
@pedido.total.must_equal 30
end
end
@elissonmichael
Copy link
Author

elissonmichael commented Jun 25, 2018

@rodrigomanhaes eu queria implementar os testes de carrinho sem que ele dependesse de produto ou pedido (sua antiga classe item).
Eu não consegui fazer isso sem alterar a classe carrinho para poder injetar o Mock no construtor.

@douglascamata
Copy link

douglascamata commented Jun 25, 2018

@elissonmichael eu injetaria uma classe para fazer a soma do preço dos itens no carrinho lá no construtor. Ai em vez de mockar pedido_class ali, você poderia injetar um somador dummy que sempre retorna um valor fixo.

class TotalizadorDummy

  def total(lista_de_pedidos)
    lista_de_pedidos.sum(&:total)
  end
end

A partir daí da pra brincar, por exemplo, com classes de desconto também, que poderiam ser injetadas no totalizador, tipo:

class DescontoBlackFriday

  def aplica(lista_de_pedidos)
    # 10% de desconto por item, até um máximo de 2 itens
    lista_de_pedidos.size % 2 / 10
  end
end

class TotalizadorDummy

  def initialize(descontos = [])
    @descontos = descontos
  end

  def total(lista_de_pedidos)
    lista_de_pedidos.sum(&:total) * total_descontos
  end

  def total_descontos
    @descontos.map { |desconto| desconto.aplica(lista_de_pedidos) }
  end
end

Atenção que minha brincadeira com descontos ai só funciona com porcentagem e é acumulativo (o total é multiplicado pela soma de todos descontos e cada desconto deve estar em forma decimal, por exemplo, 10% deve ser 0.10) e não multiplicativo.

Descontos poderiam ser decorators em cima do totalizador também, alterando como a função total funciona, não seria uma má idéia.

@elissonmichael
Copy link
Author

Obrigado @douglascamata, muito bom.

@rodrigomanhaes
Copy link

Uma alternativa é delegar a criação do pedido para uma fábrica, de modo que possa ser manipulada para retorrnar o objeto devido.

@rodrigomanhaes
Copy link

É também possível pensar as classes Carrinho e Pedido como um aggregate, e com essa visão talvez não seja necessário testá-las com um grau tão alto de isolamento.

@elissonmichael
Copy link
Author

Obrigado @rodrigomanhaes, gostei do aggregate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment