To wciągnęło mnie w testy jednostkowe i bardzo mnie ucieszyło
Właśnie zaczęliśmy przeprowadzać testy jednostkowe. Od dawna wiedziałem, że dobrze będzie zacząć, ale nie miałem pojęcia, jak zacząć i co ważniejsze, co przetestować.
Następnie musieliśmy przepisać ważny fragment kodu w naszym programie księgowym. Ta część była bardzo złożona, ponieważ obejmowała wiele różnych scenariuszy. Część o której mówię to sposób na opłacenie faktur sprzedaży i / lub zakupu już wprowadzonych do systemu księgowego.
Po prostu nie wiedziałem, jak zacząć go kodować, ponieważ było tak wiele różnych opcji płatności. Faktura może wynosić 100 USD, ale klient przelał tylko 99 USD. Być może wysłałeś faktury sprzedaży do klienta, ale dokonałeś również zakupu od tego klienta. Więc sprzedałeś go za 300 $, ale kupiłeś za 100 $. Możesz oczekiwać, że Twój klient zapłaci Ci 200 USD, aby uregulować saldo. A co, jeśli sprzedałeś za 500 USD, ale klient zapłaci Ci tylko 250 USD?
Więc miałem bardzo złożony problem do rozwiązania z wieloma możliwościami, że jeden scenariusz działałby idealnie, ale byłby błędny na innym typie kombinacji faktury / płatności.
Tutaj na ratunek przyszedł test jednostkowy.
Zacząłem pisać (w kodzie testowym) sposób tworzenia listy faktur, zarówno sprzedaży, jak i zakupów. Następnie napisałem drugą metodę tworzenia faktycznej płatności. Zwykle użytkownik wprowadzałby te informacje za pośrednictwem interfejsu użytkownika.
Następnie stworzyłem pierwszą metodę TestMethod, testując bardzo prostą płatność za jedną fakturę bez żadnych rabatów. Cała akcja w systemie miałaby miejsce, gdyby płatność bankowa została zapisana w bazie danych. Jak widać, utworzyłem fakturę, utworzyłem płatność (transakcję bankową) i zapisałem transakcję na dysku. W moich potwierdzeniach umieszczam, jakie powinny być prawidłowe liczby kończące się w transakcji bankowej i na połączonej fakturze. Po transakcji sprawdzam liczbę płatności, kwoty płatności, kwotę rabatu i saldo faktury.
Po zakończeniu testu szedłem do bazy danych i dwukrotnie sprawdzałem, czy jest tam to, czego się spodziewałem.
Po napisaniu testu zacząłem kodować metodę płatności (część klasy BankHeader). W kodowaniu zająłem się tylko kodem, aby wykonać pierwszy test. Nie myślałem jeszcze o innych, bardziej złożonych scenariuszach.
Uruchomiłem pierwszy test, naprawiłem mały błąd, dopóki mój test nie przeszedł.
Potem zacząłem pisać drugi test, tym razem z rabatem od płatności. Po napisaniu testu zmodyfikowałem metodę płatności, aby obsługiwała rabaty.
Testując poprawność z rabatem płatności, testowałem również prostą płatność. Oczywiście oba testy powinny przejść pomyślnie.
Potem przeszedłem do bardziej złożonych scenariuszy.
1) Pomyśl o nowym scenariuszu
2) Napisz test dla tego scenariusza
3) Uruchom ten pojedynczy test, aby sprawdzić, czy przejdzie
4) Gdyby tak się nie stało, debugowałbym i modyfikowałbym kod, dopóki nie przejdzie.
5) Modyfikując kod, wykonywałem wszystkie testy
W ten sposób udało mi się stworzyć moją bardzo złożoną metodę płatności. Bez testów jednostkowych nie wiedziałem, jak rozpocząć kodowanie, problem wydawał się przytłaczający. Dzięki testowaniu mogłem zacząć od prostej metody i rozszerzać ją krok po kroku, mając pewność, że prostsze scenariusze nadal będą działać.
Jestem przekonany, że zastosowanie testów jednostkowych zaoszczędziło mi kilku dni (lub tygodni) kodowania i mniej więcej gwarantuje poprawność mojej metody.
Jeśli później pomyślę o nowym scenariuszu, mogę po prostu dodać go do testów, aby sprawdzić, czy działa, czy nie. Jeśli nie, mogę zmodyfikować kod, ale nadal mam pewność, że inne scenariusze nadal działają poprawnie. Pozwoli to zaoszczędzić dni i dni w fazie konserwacji i naprawiania błędów.
Tak, nawet przetestowany kod może nadal zawierać błędy, jeśli użytkownik robi rzeczy, o których nie pomyślałeś lub uniemożliwiłeś mu zrobienie
Poniżej znajduje się tylko kilka testów, które utworzyłem, aby przetestować moją metodę płatności.
public class TestPayments
{
InvoiceDiaryHeader invoiceHeader = null;
InvoiceDiaryDetail invoiceDetail = null;
BankCashDiaryHeader bankHeader = null;
BankCashDiaryDetail bankDetail = null;
public InvoiceDiaryHeader CreateSales(string amountIncVat, bool sales, int invoiceNumber, string date)
{
......
......
}
public BankCashDiaryHeader CreateMultiplePayments(IList<InvoiceDiaryHeader> invoices, int headerNumber, decimal amount, decimal discount)
{
......
......
......
}
[TestMethod]
public void TestSingleSalesPaymentNoDiscount()
{
IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
list.Add(CreateSales("119", true, 1, "01-09-2008"));
bankHeader = CreateMultiplePayments(list, 1, 119.00M, 0);
bankHeader.Save();
Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
Assert.AreEqual(1, bankHeader.BankCashDetails[0].Payments.Count);
Assert.AreEqual(119M, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
Assert.AreEqual(0M, bankHeader.BankCashDetails[0].Payments[0].PaymentDiscount);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
}
[TestMethod]
public void TestSingleSalesPaymentDiscount()
{
IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
list.Add(CreateSales("119", true, 2, "01-09-2008"));
bankHeader = CreateMultiplePayments(list, 2, 118.00M, 1M);
bankHeader.Save();
Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
Assert.AreEqual(1, bankHeader.BankCashDetails[0].Payments.Count);
Assert.AreEqual(118M, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
Assert.AreEqual(1M, bankHeader.BankCashDetails[0].Payments[0].PaymentDiscount);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
}
[TestMethod]
[ExpectedException(typeof(ApplicationException))]
public void TestDuplicateInvoiceNumber()
{
IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
list.Add(CreateSales("100", true, 2, "01-09-2008"));
list.Add(CreateSales("200", true, 2, "01-09-2008"));
bankHeader = CreateMultiplePayments(list, 3, 300, 0);
bankHeader.Save();
Assert.Fail("expected an ApplicationException");
}
[TestMethod]
public void TestMultipleSalesPaymentWithPaymentDiscount()
{
IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
list.Add(CreateSales("119", true, 11, "01-09-2008"));
list.Add(CreateSales("400", true, 12, "02-09-2008"));
list.Add(CreateSales("600", true, 13, "03-09-2008"));
list.Add(CreateSales("25,40", true, 14, "04-09-2008"));
bankHeader = CreateMultiplePayments(list, 5, 1144.00M, 0.40M);
bankHeader.Save();
Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
Assert.AreEqual(4, bankHeader.BankCashDetails[0].Payments.Count);
Assert.AreEqual(118.60M, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
Assert.AreEqual(400, bankHeader.BankCashDetails[0].Payments[1].PaymentAmount);
Assert.AreEqual(600, bankHeader.BankCashDetails[0].Payments[2].PaymentAmount);
Assert.AreEqual(25.40M, bankHeader.BankCashDetails[0].Payments[3].PaymentAmount);
Assert.AreEqual(0.40M, bankHeader.BankCashDetails[0].Payments[0].PaymentDiscount);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[1].PaymentDiscount);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[2].PaymentDiscount);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[3].PaymentDiscount);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[1].InvoiceHeader.Balance);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[2].InvoiceHeader.Balance);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[3].InvoiceHeader.Balance);
}
[TestMethod]
public void TestSettlement()
{
IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
list.Add(CreateSales("300", true, 43, "01-09-2008"));
list.Add(CreateSales("100", false, 6453, "02-09-2008"));
bankHeader = CreateMultiplePayments(list, 22, 200, 0);
bankHeader.Save();
Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
Assert.AreEqual(2, bankHeader.BankCashDetails[0].Payments.Count);
Assert.AreEqual(300, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
Assert.AreEqual(-100, bankHeader.BankCashDetails[0].Payments[1].PaymentAmount);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[1].InvoiceHeader.Balance);
}