有人能帮助将遗留代码转换为TDD代码吗?初学者拔头发

本文关键字:代码 TDD 初学者 头发 转换 帮助 | 更新日期: 2023-09-27 18:21:41

我正在尝试使用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);
        }
    }

有人能帮助将遗留代码转换为TDD代码吗?初学者拔头发

在Jon,你可能为了一个角落而吃得太多了。用一个新特性而不是现有特性来实现TDD。在这个场景中,您不仅引入了测试、TDD和嘲讽。但现在您也引入了重构(又是一个新概念)。

一旦您掌握了关于greenfield代码的TDD的基础知识,就可以将其应用于brownfield代码以及重构。

最后一点:TDD不需要mocking,mocking是一个单独的概念,可以很好地与TDD配合使用,但它不是必需的。简单地说,TDD就是通过测试来推动设计。

为了有效地进行单元测试,您应该遵守一些设计原则。松耦合、关注点分离和依赖注入。原因是,如果没有,你将无法在测试中使用模拟(这是你需要做的)

我注意到在你上面的代码中的第一个方法:

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

由此我推测,您实际上实例化了查询DB所需的一切。这不是最佳实践,也可能是问题的根源。你应该依赖抽象,而不是具体化。应使用构造函数注入传递"存储库"接口(或类似接口)的实例。

一本让你开始的好书是:http://www.amazon.co.uk/Professional-ASP-NET-Design-Patterns-Millett/dp/0470292784

希望这能有所帮助。