2
\$\begingroup\$

I was told that this version is too simple, that need something more complex. I added Entity Framework.

Here my code:

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.ComponentModel.DataAnnotations;
using System.Linq;

namespace SalesTaxes
{

    public class ShoppingCartContext : DbContext
    {
        public DbSet<ShoppingCart> Carts { get; set; }
        public DbSet<ShoppingCartItem> Items { get; set; }
        public DbSet<Product> Products { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {

            using (var db = new ShoppingCartContext())
            {
                var basket = 0;
                Console.WriteLine("Please enter basket number: 1, 2 or 3:");
                try
                {
                    basket = Int32.Parse(Console.ReadLine());
                }
                catch (FormatException e)
                {
                    Console.WriteLine(e.Message);
                }

                var cart = new ShoppingCart(GetItemsList(basket));               
                cart.Name = "Basket #" + basket.ToString();

                db.Carts.Add(cart);
                db.SaveChanges();

                cart.PrintReceipt(basket);
                Console.WriteLine("Press any key to exit...");
                Console.ReadKey();
            }            
        }

        private static List<ShoppingCartItem> GetItemsList(int basket)
        {
            switch (basket)
            {
                case 1:
                    return new List<ShoppingCartItem>
                    {
                        new ShoppingCartItem(new Product( "Book", 12.49m, Product.ProductType.Book, false), 1),
                        new ShoppingCartItem(new Product("music CD", 14.99m, Product.ProductType.Other, false), 1),
                        new ShoppingCartItem(new Product("chocolate bar", 0.85m, Product.ProductType.Food, false), 1)
                    };                    

                case 2:
                    return new List<ShoppingCartItem>
                    {
                        new ShoppingCartItem(new Product("imported box of chocolates", 10.00m, Product.ProductType.Food, true), 1),
                        new ShoppingCartItem(new Product("imported bottle of perfume", 47.50m, Product.ProductType.Other, true), 1)                        
                    };

                case 3:
                    return new List<ShoppingCartItem>
                    {                        
                        new ShoppingCartItem(new Product("imported bottle of perfume", 27.99m, Product.ProductType.Other, true), 1),
                        new ShoppingCartItem(new Product("bottle of perfume", 18.99m, Product.ProductType.Other, false), 1),
                        new ShoppingCartItem(new Product("packet of headache pills", 9.75m, Product.ProductType.Medical, false), 1),
                        new ShoppingCartItem(new Product("box of imported chocolates", 11.25m, Product.ProductType.Food, true), 1),
                    };

                default:
                    return new List<ShoppingCartItem>();

        }
        }
    }

    public class ShoppingCart
    {
        public ShoppingCart(List<ShoppingCartItem> items)
        {
            Items = items;
            TotalTax = Items.Sum(x => x.Taxes);
            Total = Items.Sum(x => x.Price) + TotalTax;
        }

        [Key]
        public int CartID { get; set; }

        public string Name { get; set; }

        public virtual IList<ShoppingCartItem> Items { get; set; }

        public decimal TotalTax { get; set; }

        public decimal Total { get; set; }

        public void PrintReceipt(int basket)
        {            
            Console.WriteLine("Receipt {0}", basket);

            foreach (var item in Items)
            {
                Console.WriteLine("{0} {1}:  ${2} ", item.Quantity, item.Item.Name, item.Price + item.Taxes);               
            }                      

            Console.WriteLine("Total Taxes: ${0} ", TotalTax);
            Console.WriteLine("Total: ${0} ", Total);
        }
    }   

    public class ShoppingCartItem
    {
        const decimal TaxRate = 0.1m;
        const decimal ImpTaxRate = 0.05m;

        public ShoppingCartItem(Product product,int quantity)
        {
            Item = product;
            Quantity = quantity;
            Price = Quantity * Item.Price;
            Taxes = Quantity*Math.Round(Item.Price * (CalculateTaxRate() + CalculateImportRate()), 2);
        }

        [Key]
        public int ItemID { get; set; }

        public virtual Product Item { get; set; }

        public int Quantity { get; set; }

        public decimal Price { get; set; }

        public decimal Taxes { get; set; }

        private decimal CalculateTaxRate()
        {
            return Item.IsExempt
                ? 0
                : TaxRate;
        }

        private decimal CalculateImportRate()
        {
            return Item.IsImport
                ? ImpTaxRate
                : 0;
        }
    }    

    public class Product
    {
        public Product(string name, decimal price, ProductType type, bool isimport)
        {
            Name = name;
            Price = price;
            Type = type;
            IsImport = isimport;
            IsExempt = (Type != ProductType.Other);
        }

        public enum ProductType
        {
            Food = 1,
            Book = 2,
            Medical = 3,
            Other = 4
        };

        [Key]
        public int ProductID { get; set; }

        public string Name { get; set; }

        public decimal Price { get; set; }

        public ProductType Type { get; set; }

        public bool IsImport { get; set; }

        public bool IsExempt { get; set; }       
    }
}

I need also Unit Testing, but I'm not sure, because I almost don't have methods here.

But anyway this is my try:

using Microsoft.VisualStudio.TestTools.UnitTesting;
using SalesTaxes;

namespace UnitTests
{
    [TestClass]
    public class SalestaxesUnitTest
    {

        [TestMethod]
        public void TestProduct1()
        {
            var product1 = new Product("Book", 12.49m,Product.ProductType.Book, false);
            var item1 = new ShoppingCartItem(product1, 1);

            var actual = item1.Taxes;
            decimal expected = 0.00m;
            Assert.AreEqual(expected, actual, "Should be zero taxes");
        }

        [TestMethod]
        public void TestProduct2()
        {
            var product1 = new Product("music CD", 14.99m, Product.ProductType.Other,false);

            var item1 = new ShoppingCartItem(product1, 1);

            var actual = item1.Taxes;
            decimal expected = 1.5m;
            Assert.AreEqual(expected, actual, "Wrong taxes");
        }

        [TestMethod]
        public void TestProduct3()
        {
            var product1 = new Product("imported box of chocolates", 10.00m, Product.ProductType.Food, true);

            var item1 = new ShoppingCartItem(product1, 1);

            var actual = item1.Taxes;
            decimal expected = 0.50m;
            Assert.AreEqual(expected, actual, "Wrong taxes");
        }

        [TestMethod]
        public void TestProduct4()
        {
            var product1 = new Product("imported bottle of perfume", 47.50m, Product.ProductType.Other, true);

            var item1 = new ShoppingCartItem(product1, 1);

            var actual = item1.Taxes;
            decimal expected = 7.12m;
            Assert.AreEqual(expected, actual, "Wrong taxes");
        }
    }
}

Maybe I need to use MOQ Framework for Unit Testing, but not sure how it can be used in this case.

\$\endgroup\$
1
  • \$\begingroup\$ I was told that this version is too simple, that need something more complex.... Holy OO opposite day, Batman. I can hardly believe I just read that. \$\endgroup\$
    – radarbob
    Nov 17, 2015 at 16:52

2 Answers 2

1
\$\begingroup\$

Several observations:

  • Writing unit tests for mathematical calculations is hard if you're not using something that will test a range of inputs (like FsCheck in F#). If you don't, you have to think of all the possible permutations of your function domain yourself.
  • You might not want to put the calculation logic on the entities themselves, though sometimes it makes sense to do so to keep things simple.
  • My recommendation is to break calculations into stateless static methods and use Pex or FsCheck depending on your language of choice.
  • Since you're testing calculations, you shouldn't need to mock anything since all inputs should be directly provided to your method.
\$\endgroup\$
0
\$\begingroup\$
  1. I don't know what your code is supposed to be doing or really what you problem is.
  2. Your test cases are all the same code and could be refactored into a single function.
  3. Your test cases don't define what you are testing, I think this might be because you keyboard is broken, I suggest you buy a new one with a working / key.
  4. Can decimal hold a negative value? If so slap a few negative price items in there and watch it crash.
  5. Same for quantity.
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.