Может ли кто-нибудь помочь преобразовать устаревший код в код TDD? Начинающий вырывает волосы - PullRequest
0 голосов
/ 27 января 2012

Я пытаюсь переписать метод с использованием TDD.

Вот оригинальный метод без логики базы данных, а ниже - моя попытка переписать его. Это не завершено, так как я просто ударил стену и не понимаю, как это закончить.

Я пытаюсь учиться и не могу разобраться, поэтому мне нужна помощь. Спасибо за чтение.

    //Original Method

    public class StringCalculator()
    {

    public List<ShipmentInformation> GetBoxRange(int BoxSize, int Quantity, ref string DVDType, ref string ErrorMessage)
    {

        //Get Connection String


        //Do Database Queries and return shipmentInfo variable as List<ShipmentInformation>

        var DVDTotals = shipmentInfo.GroupBy(x => x.DVDType).Select(x => new { DVDType = x.Key, Total = x.Sum(y => (y.DVDEndID - y.DVDStartID) + 1) });

        if (DVDTotals.Count() == 0 || DVDTotals.All(x => x.Total < Quantity))
        {
            ErrorMessage = "There is not enough data to create a shipment based on the quantity";
            return new List<ShipmentInformation>();
        }


        //Select the one with the largest amount of stock
        var LargestDVDType = DVDTotals.Aggregate((l, r) => l.Total > r.Total ? l : r).DVDType;

        var LargestDVDTypeSelection = shipmentInfo.Where(x => x.DVDType == LargestDVDType);

        long previousDVDStartID = 0;

        //Make sure ranges are sequential
        List<ShipmentInformation> SequentialBoxResults = new List<ShipmentInformation>();
        foreach (var item in LargestDVDTypeSelection)
        {
            if (item.DVDStartID - previousDVDStartID == BoxSize || previousDVDStartID == 0) 
            {
                SequentialBoxResults.Add(item);
            }
            else
            {
                SequentialBoxResults.Clear();
                SequentialBoxResults.Add(item);
            }

            previousDVDStartID = item.DVDStartID;

            if (BoxSize * SequentialBoxResults.Count == Quantity)
            {
                break;
            }
        }

        if (SequentialBoxResults.Count == 0 || BoxSize * SequentialBoxResults.Count != Quantity)
        {
            ErrorMessage = "There are no available ranges to create a shipment based on the quantity";
            return new List<ShipmentInformation>(); ;
        }

        List<ShipmentInformation> Results = new List<ShipmentInformation>();
        var Genres = SequentialBoxResults.GroupBy(x => x.Genre);
        foreach (var Genre in Genres)
        {
            var orderedGenres = Genre.OrderBy(x => x.DVDStartID);

            ShipmentInformation shipment = new ShipmentInformation();
            shipment.Genre = Genre.Key;
            shipment.DVDStartID = orderedGenres.First().DVDStartID;
            var lastItem = orderedGenres.Last();
            shipment.DVDEndID = lastItem.DVDEndID;
            shipment.DVDType = lastItem.DVDType;

            Results.Add(shipment);
        }

        //We have our shipment recordsnow split them up if any are over 75000

        for (int i = 0; i < Results.Count; i++)
        {
            long rangeSize = Results[i].DVDEndID - Results[i].DVDStartID + 1;

            double noOfLoops = Math.Ceiling(rangeSize / 75000D);
            long remainder = rangeSize % 75000;

            if (noOfLoops > 1)
            {
                bool AddedRecord = false;
                for (int j = 0; j < noOfLoops; j++)
                {
                    long shipmentSize = 0;

                    if (j == (noOfLoops - 1))
                    {
                        shipmentSize = remainder;
                        if (AddedRecord)
                            Results.Add(new ShipmentInformation() { DVDStartID = Results.Last().DVDEndID + 1, DVDEndID = Results.Last().DVDEndID + 1 + (shipmentSize - 1), Genre = Results.Last().Genre });
                        else
                            Results.Add(new ShipmentInformation() { DVDStartID = Results[i].DVDEndID + 1, DVDEndID = Results[i].DVDEndID + 1 + (shipmentSize - 1), Genre = Results[i].Genre });
                    }
                    else
                    {
                        shipmentSize = 75000;
                        if (j == 0)
                        {
                            Results[i].DVDEndID = Results[i].DVDStartID + (shipmentSize - 1);
                        }
                        else if (j == 1)
                        {
                            Results.Add(new ShipmentInformation() { DVDStartID = Results[i].DVDEndID + 1, DVDEndID = Results[i].DVDEndID + 1 + (shipmentSize - 1), Genre = Results[i].Genre });
                            AddedRecord = true;
                        }
                        else
                        {
                            Results.Add(new ShipmentInformation() { DVDStartID = Results.Last().DVDEndID + 1, DVDEndID = Results.Last().DVDEndID + 1 + (shipmentSize - 1), Genre = Results.Last().Genre });
                            AddedRecord = true;
                        }
                    }
                }
            }
        }

        return Results;

    }
  }

   //New Method with Tests

  public List<ShipmentInformation> GetBoxRange(int BoxSize, int Quantity, DateTime CutOffDate, IDataProvider Provider, ref string DVDType, ref string ErrorMessage)
    {
        if (BoxSize == 0)
        {
            ErrorMessage = "Please enter a BoxSize";
        }

        if (Quantity == 0)
        {
            ErrorMessage = "Please enter a Quantity";
        }


        if (!String.IsNullOrWhiteSpace(ErrorMessage))
        {
            return new List<ShipmentInformation>();
        }

        List<ShipmentInformation> Data = Provider.GetData();

        if (Data.Count == 0)
        {
            ErrorMessage = "Database failed to return data";
            return new List<ShipmentInformation>();
        }

        List<ShipmentInformation> OrderedData = GetSequentialBoxes(Data, BoxSize, Quantity);

        if (OrderedData.Count == 0)
        {
            ErrorMessage = "No sequential data found";
            return new List<ShipmentInformation>();
        }

        //I'm not sure how to continue from here - I started writing GetRecordsPerGenre but got lost in what I'm trying to do. I still need to check if size is over 75000

        return OrderedData;
    }

    public virtual List<ShipmentInformation> GetSequentialBoxes(List<ShipmentInformation> Data, int BoxSize, int Quantity)
    {
        if (Data.Count == 0)
            return new List<ShipmentInformation>();

        var orderedData = Data.OrderBy(x => x.DVDStartID);
        if (!orderedData.SequenceEqual(Data))
            return new List<ShipmentInformation>();

        var DVDTotals = Data.GroupBy(x => x.DVDType).Select(x => new { DVDType = x.Key, Total = x.Sum(y => (y.DVDEndID - y.DVDStartID) + 1) });

        if (DVDTotals.Count() == 0 || DVDTotals.All(x => x.Total < Quantity))
        {
            return new List<ShipmentInformation>();
        }

        var LargestDVDType = DVDTotals.Aggregate((l, r) => l.Total > r.Total ? l : r).DVDType;

        Data = Data.Where(x => x.DVDType == LargestDVDType).ToList();

        List<ShipmentInformation> returnData = new List<ShipmentInformation>();
        long previousDVDStartID = 0;
        foreach (var item in Data)
        {
            if (previousDVDStartID == 0 || item.DVDStartID - previousDVDStartID == BoxSize)
            {
                returnData.Add(item);
            }
            else
            {
                returnData.Clear();
                returnData.Add(item);
            }

            previousDVDStartID = item.DVDStartID;

            if (returnData.Count * BoxSize == Quantity)
                break;
        }

        return returnData.OrderBy(x => x.DVDStartID).ToList();
    }

    public List<ShipmentInformation> GetRecordsPerGenre(List<ShipmentInformation> Data)
    {
        List<ShipmentInformation> Results = new List<ShipmentInformation>();
        var Genres = Data.GroupBy(x => x.Genre);
        foreach (var Genre in Genres)
        {
            var orderedGenres = Genre.OrderBy(x => x.DVDStartID);

            ShipmentInformation shipment = new ShipmentInformation();
            shipment.Genre = Genre.Key;
            shipment.DVDStartID = orderedGenres.First().DVDStartID;
            var lastItem = orderedGenres.Last();
            shipment.DVDEndID = lastItem.DVDEndID;
            shipment.DVDType = lastItem.DVDType;

            Results.Add(shipment);
        }

        return Results;
    }




   //Tests

   [TestFixture]
    public class GetBoxRangeMethod
    {
        private StringCalculator CreateNewCalculator()
        {
            return new StringCalculator();
        }

        [TestCase(0, 1)]
        [TestCase(1, 0)]
        public void ZeroValuesForBoxSizeOrQuantity_ReturnsErrorAndEmptyList(int BoxSize, int Quantity)
        {
            StringCalculator sc = CreateNewCalculator();

            string ErrorMessage = "";
            string ChipType = "";
            FakeDataProvier provider = new FakeDataProvier(new List<ShipmentInformation>());
            List<ShipmentInformation> result = sc.GetBoxRange(BoxSize, Quantity, new DateTime(2012, 01, 01), "A", provider, ref ChipType, ref ErrorMessage);

            Assert.AreNotEqual("", ErrorMessage);
            Assert.AreEqual(0, result.Count);
        }

        [Test]
        public void EmptyBookTypeString_ReturnsErrorAndEmptyList()
        {
            StringCalculator sc = CreateNewCalculator();

            string ErrorMessage = "";
            string ChipType = "";

            FakeDataProvier provider = new FakeDataProvier(new List<ShipmentInformation>());

            List<ShipmentInformation> result = sc.GetBoxRange(1, 1, new DateTime(2012, 01, 01), "", provider, ref ChipType, ref ErrorMessage);

            Assert.AreNotEqual("", ErrorMessage);
            Assert.AreEqual(0, result.Count);
        }

        [Test]
        public void EmptyDBData_ReturnsErrorAndEmptyList()
        {
            StringCalculator sc = CreateNewCalculator();

            string ErrorMessage = "";
            string ChipType = "";
            FakeDataProvier provider = new FakeDataProvier(new List<ShipmentInformation>());

            List<ShipmentInformation> result = sc.GetBoxRange(1, 1, new DateTime(2012, 01, 01), "A", provider, ref ChipType, ref ErrorMessage);

            Assert.AreNotEqual("", ErrorMessage);
            Assert.AreEqual(0, result.Count);
        }

        [Test]
        public void EmptyOrderedData_ReturnsErrorAndEmptyList()
        {
            FakeShipmentCalculator sc = new FakeShipmentCalculator();

            string ErrorMessage = "";
            string ChipType = "";
            FakeDataProvier provider = new FakeDataProvier(new List<ShipmentInformation>() { new ShipmentInformation() { ChipType = "A", StartPP = 1, EndPP = 2, JacketNo = "A", ReqNo = "B" } });

            List<ShipmentInformation> result = sc.GetBoxRange(1, 1, new DateTime(2012, 01, 01), "A", provider, ref ChipType, ref ErrorMessage);

            Assert.AreNotEqual("", ErrorMessage);
            Assert.AreEqual(0, result.Count);
        }

    }

    //Integration test
    [TestFixture]
    public class GetDataMethod
    {

    }

    [TestFixture]
    public class GetSequentialBoxes
    {
        private StringCalculator CreateNewCalculator()
        {
            return new StringCalculator();
        }

        [Test]
        public void MethodCalled_DoesntReturnNull()
        {
            StringCalculator sc = CreateNewCalculator();

            List<ShipmentInformation> result = sc.GetSequentialBoxes(new List<ShipmentInformation>(), 100, 1);

            Assert.IsNotNull(result);
        }

        [Test]
        public void EmptyDataPassedIn_ReturnsEmptyData()
        {
            StringCalculator sc = CreateNewCalculator();

            List<ShipmentInformation> result = sc.GetSequentialBoxes(new List<ShipmentInformation>(), 100, 1);

            Assert.AreEqual(0, result.Count);
        }

        [Test]
        public void OrderedData_ReturnsOrderedData()
        {
            StringCalculator sc = CreateNewCalculator();

            List<ShipmentInformation> inputData = new List<ShipmentInformation>();

            ShipmentInformation data = new ShipmentInformation() { ChipType = "A", StartPP = 1, EndPP = 2, JacketNo = "A", ReqNo = "B" };
            inputData.Add(data);

            data = new ShipmentInformation() { ChipType = "A", StartPP = 3, EndPP = 4, JacketNo = "A", ReqNo = "B" };
            inputData.Add(data);

            List<ShipmentInformation> result = sc.GetSequentialBoxes(inputData, 2, 1);

            ShipmentInformation firstItem = result.FirstOrDefault();
            ShipmentInformation secondItem = result.LastOrDefault();

            Assert.IsTrue(firstItem.StartPP == 1 && secondItem.StartPP == 3);
        }

        [Test]
        public void UnOrderedData_ReturnsEmptyData()
        {
            StringCalculator sc = CreateNewCalculator();

            List<ShipmentInformation> inputData = new List<ShipmentInformation>();

            ShipmentInformation data = new ShipmentInformation() { ChipType = "A", StartPP = 3, EndPP = 4, JacketNo = "A", ReqNo = "B" };
            inputData.Add(data);

            data = new ShipmentInformation() { ChipType = "A", StartPP = 1, EndPP = 2, JacketNo = "A", ReqNo = "B" };
            inputData.Add(data);

            int NUMBER_GREATER_THAN_ONE = 2;
            List<ShipmentInformation> result = sc.GetSequentialBoxes(inputData, NUMBER_GREATER_THAN_ONE, 1);

            Assert.AreEqual(0, result.Count);
        }

        [Test]
        public void SequenceJumps_ClearsListAndStartsAgain()
        {
            StringCalculator sc = CreateNewCalculator();

            List<ShipmentInformation> inputData = new List<ShipmentInformation>();

            ShipmentInformation data = new ShipmentInformation() { ChipType = "A", StartPP = 1, EndPP = 2, JacketNo = "A", ReqNo = "B" };
            inputData.Add(data);

            data = new ShipmentInformation() { ChipType = "A", StartPP = 5, EndPP = 6, JacketNo = "A", ReqNo = "B" };
            inputData.Add(data);

            List<ShipmentInformation> result = sc.GetSequentialBoxes(inputData, 2, 1);

            Assert.IsTrue(result.First().StartPP == 5);
        }

        [Test]
        public void LargestNumberOfItemsWithSameChipType_ReturnsDataWithOnlyThatChipType()
        {
            StringCalculator sc = CreateNewCalculator();

            List<ShipmentInformation> inputData = new List<ShipmentInformation>();

            ShipmentInformation data = new ShipmentInformation() { ChipType = "A", StartPP = 1, EndPP = 2, JacketNo = "A", ReqNo = "B" };
            inputData.Add(data);

            data = new ShipmentInformation() { ChipType = "A", StartPP = 3, EndPP = 4, JacketNo = "A", ReqNo = "B" };
            inputData.Add(data);

            data = new ShipmentInformation() { ChipType = "B", StartPP = 5, EndPP = 6, JacketNo = "A", ReqNo = "B" };
            inputData.Add(data);

            int BoxSizeSlashSequenceJumpAllowed = 2;

            List<ShipmentInformation> result = sc.GetSequentialBoxes(inputData, BoxSizeSlashSequenceJumpAllowed, 1);

            Assert.IsTrue(result.All(x => x.ChipType == "A"));
        }

        [Test]
        public void TotalNumberOfRecordsPerChipTypeLessThanQuantity_ReturnsEmptyData()
        {
            StringCalculator sc = CreateNewCalculator();

            List<ShipmentInformation> inputData = new List<ShipmentInformation>();

            ShipmentInformation data = new ShipmentInformation() { ChipType = "A", StartPP = 1, EndPP = 2, JacketNo = "A", ReqNo = "B" };
            inputData.Add(data);

            data = new ShipmentInformation() { ChipType = "A", StartPP = 3, EndPP = 4, JacketNo = "A", ReqNo = "B" };
            inputData.Add(data);

            data = new ShipmentInformation() { ChipType = "B", StartPP = 5, EndPP = 6, JacketNo = "A", ReqNo = "B" };
            inputData.Add(data);

            int BoxSizeSlashSequenceJumpAllowed = 2;
            int Quantity = 5;

            List<ShipmentInformation> result = sc.GetSequentialBoxes(inputData, BoxSizeSlashSequenceJumpAllowed, Quantity);

            Assert.AreEqual(0, result.Count);
        }

        [Test]
        public void DataReturned_WhenQuantityReached()
        {
            StringCalculator sc = CreateNewCalculator();

            List<ShipmentInformation> inputData = new List<ShipmentInformation>();

            ShipmentInformation data = new ShipmentInformation() { ChipType = "A", StartPP = 1, EndPP = 2, JacketNo = "A", ReqNo = "B" };
            inputData.Add(data);

            data = new ShipmentInformation() { ChipType = "A", StartPP = 3, EndPP = 4, JacketNo = "A", ReqNo = "B" };
            inputData.Add(data);

            data = new ShipmentInformation() { ChipType = "A", StartPP = 5, EndPP = 6, JacketNo = "A", ReqNo = "B" };
            inputData.Add(data);

            int BoxSizeSlashSequenceJumpAllowed = 2;
            int Quantity = 4;

            List<ShipmentInformation> result = sc.GetSequentialBoxes(inputData, BoxSizeSlashSequenceJumpAllowed, Quantity);

            Assert.AreEqual(2, result.Count);
        }
    }

    [TestFixture]
    public class GetRecordsPerGenre
    {
        private StringCalculator CreateNewCalculator()
        {
            return new StringCalculator();
        }

        [Test]
        public void MethodCalled_DoesntReturnNull()
        {
            StringCalculator sc = CreateNewCalculator();

            List<ShipmentInformation> result = sc.GetRecordsPerSerialRange(new List<ShipmentInformation>());

            Assert.IsNotNull(result);
        }

        [Test]
        public void EmptyDataPassedIn_ReturnsEmptyData()
        {
            StringCalculator sc = CreateNewCalculator();

            List<ShipmentInformation> result = sc.GetRecordsPerSerialRange(new List<ShipmentInformation>());

            Assert.AreEqual(0, result.Count);
        }

        [Test]
        public void Data_ReturnsGroupedByData()
        {
            StringCalculator sc = CreateNewCalculator();

            List<ShipmentInformation> inputData = new List<ShipmentInformation>();

            ShipmentInformation data = new ShipmentInformation();
            data.ChipType = "A";
            data.ReqNo = "B";
            data.JacketNo="C";
            data.StartPP = 1;
            data.EndPP = 2;

            inputData.Add(data);

            data = new ShipmentInformation();

            data.ChipType = "A";
            data.ReqNo = "B";
            data.JacketNo = "C";
            data.StartPP = 1;
            data.EndPP = 2;

            inputData.Add(data);

            List<ShipmentInformation> result = sc.GetRecordsPerSerialRange(inputData);

            Assert.AreEqual(1, result.Count);
        }
    }

Ответы [ 2 ]

2 голосов
/ 27 января 2012

В Джоне вы можете слишком сильно откусывать за новичка. внедрить TDD с новой функцией, а не с существующей. С этим сценарием вы не только вводите тестирование, TDD, насмешку. но теперь вы также ввели рефакторинг (еще одна новая концепция).

Как только вы ознакомитесь с основами TDD по коду с нуля, вы можете применить его к коду с нуля и рефакторинг.

И последнее замечание: TDD не требует насмешек, это отдельная концепция, которая хорошо работает с TDD, но не обязательна. Проще говоря, TDD: разрабатывает дизайн с помощью тестов.

2 голосов
/ 27 января 2012

Для эффективного модульного тестирования необходимо придерживаться некоторых принципов проектирования.Слабая связь, разделение проблем и внедрение зависимостей.Причина этого в том, что без этого вы не сможете использовать макеты в своих тестах (что вам нужно будет сделать)

Я заметил, что в первом из приведенных выше методов у вас был код:

//Get Connection String
//Do Database Queries and return shipmentInfo variable as List<ShipmentInformation>

Исходя из этого, я предполагаю, что вы на самом деле создаете все, что вам нужно здесь для запроса к БД.Это не лучшая практика и, вероятно, корень ваших проблем.Вы должны зависеть от абстракций, а не от конкреций.Экземпляр интерфейса «хранилище» (или аналогичный) должен передаваться с помощью инжектора Constructor.

Хорошая книга для начала будет такой: http://www.amazon.co.uk/Professional-ASP-NET-Design-Patterns-Millett/dp/0470292784

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...