-
-
Save elissonmichael/211c2c4f480ea12d19aed68843ade173 to your computer and use it in GitHub Desktop.
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 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.
Obrigado @douglascamata, muito bom.
Uma alternativa é delegar a criação do pedido para uma fábrica, de modo que possa ser manipulada para retorrnar o objeto devido.
É 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.
Obrigado @rodrigomanhaes, gostei do aggregate.
@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.