I understand the idea behind test-driven development, write tests first, code against tests until it's successful. It's just not coming together for me in my workflow yet.
Can you give me some examples where unit-tests could be used in a front or back end web development context?
A typical unit test contains 3 phases: First, it initializes a small piece of an application it wants to test (also known as the system under test, or SUT), then it applies some stimulus to the system under test (usually by calling a method on it), and finally, it observes the resulting behavior.
A unit test is a way of testing a unit - the smallest piece of code that can be logically isolated in a system. In most programming languages, that is a function, a subroutine, a method or property. The isolated part of the definition is important.
You didn't specify a language, so I'll try and keep this somewhat generic. It'll be hard though, since it's a lot easier to express concepts with actual code.
Unit tests can be a bit confusing at first. Sometimes it's not always clear how to test something, or what the purpose of a test is.
I like to treat unit testing as a way to test small individual pieces of code.
The first place I use unit tests is to verify some method works like I expect it to for all cases. I just recently wrote a validation method for a phone number for my site. I accept any inputs, from 123-555-1212, (123) 555-1212, etc. I want to make sure my validation method works for all the possible formats. Without a unit test, I'd be forced to manually enter each different format, and check that the form posts correctly. This is very tedious and error prone. Later on, if someone makes a change to the phone validation code, it would be nice if we could easily check to make sure nothing else broke. (Maybe we added support for the country code). So, here's a trivial example:
public class PhoneValidator { public bool IsValid(string phone) { return UseSomeRegExToTestPhone(phone); } }
I could write a unit test like this:
public void TestPhoneValidator() { string goodPhone = "(123) 555-1212"; string badPhone = "555 12" PhoneValidator validator = new PhoneValidator(); Assert.IsTrue(validator.IsValid(goodPhone)); Assert.IsFalse(validator.IsValid(badPhone)); }
Those 2 Assert lines will verify that the value returned from IsValid() is true and false respectively.
In the real world, you would probably have lots and lots of examples of good and bad phone numbers. I have about 30 phone numbers that I test against. Simply running this unit test in the future will tell you if your phone validation logic is broke.
We can also use unit tests to simulate things outside of our control.
Unit tests should run independent of any outside resources. Your tests shouldn't depend on a database being present, or a web service being available. So instead, we simulate these resources, so we can control what they return. In my app for example, I can't simulate a rejected credit card on registration. The bank probably would not like me submitting thousands of bad credit cards just to make sure my error handling code is correct. Here's some sample code:
public class AccountServices { private IBankWebService _webService = new BankWebService(); public string RegisterUser(string username, string creditCard) { AddUserToDatabase(username); bool success = _webService.BillUser(creditCard); if (success == false) return "Your credit card was declined" else return "Success!" } }
This is where unit testing is very confusing and not obvious. What should a test of this method do? The first thing, it would be very nice if we could check to see that if billing failed, the appropriate error message was returned. As it turns out, by using a mock, there's a way. We use what's called Inversion of Control. Right now, AccountServices() is responsible for creating the BankWebService object. Let's let the caller of this class supply it though:
public class AccountServices { public AccountServices(IBankWebService webService) { _webService = webService; } private IBankWebService _webService; public string RegisterUser(string username, string creditCard) { AddUserToDatabase(username); bool success = _webService.BillUser(creditCard); if (success == false) return "Your credit card was declined" else return "Success!" } }
Because the caller is responsible for creating the BankWebService object, our Unit test can create a fake one:
public class FakeBankWebService : IBankWebService { public bool BillUser(string creditCard) { return false; // our fake object always says billing failed } } public void TestUserIsRemoved() { IBankWebService fakeBank = FakeBankWebService(); AccountServices services = new AccountServices(fakeBank); string registrationResult = services.RegisterUser("test_username"); Assert.AreEqual("Your credit card was declined", registrationResult); }
By using that fake object, anytime our bank's BillUser() is called, our fake object will always return false. Our unit test now verifies that if the call to the bank fails, RegisterUser() will return the correct error message.
Suppose one day you are making some changes, and a bug creeps in:
public string RegisterUser(string username, string creditCard) { AddUserToDatabase(username); bool success = _webService.BillUser(creditCard); if (success) // IT'S BACKWARDS NOW return "Your credit card was declined" else return "Success!" }
Now, when your billing fails, Your RegisterUser() method returns "Success!". Fortunately, you have a unit test written. That unit test will now fail because it's no longer returning "Your credit card was declined".
It's much easier and quicker to find the bug this way than to manually fill out your registration form with a bad credit card, just to check the error message.
Once you look at different mocking frameworks, there are even more powerful things you can do. You can verify your fake methods were called, you can verify the number of times a method was called, you can verify the parameters that methods were called with, etc.
I think once you understand these 2 ideas, you will understand more than enough to write plenty of unit tests for your project.
If you tell us the language you're using, we can direct you better though.
I hope this helps. I apologize if some of it is confusing. I'll clean it up if something doesn't make sense.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With