We're all still figuring this stuff out.
Acceptance, integration, functional, unit, isolated, selenium, system tests, TDD, BDD, etc.
The most accessible field in science, from the point of view of language, is astrophysics. What do you call spots on the sun? Sunspots. Regions of space you fall into and you don’t come out of? Black holes. Big red stars? Red giants.
— Neil Degrasse Tyson
The tests are a program that verifies that the system works as specified. But the system is not a program that verifies that the tests execute correctly.
— Robert "Uncle Bob" Martin
Think of "acceptance tests" as tests that describe the client's expectations.
"When I visit the login page and fill in my credentials, then I should be sent to the Dashboard."
[TestMethod]
public void TestValidLoginRedirectsToDashboard()
{
IWebDriver driver = new PhantomJSDriver();
Selenium selenium = new WebDriverBackedSelenium(driver, "http://localhost");
// Goto the Target website
selenium.Open("http://localhost/login");
// Fill out and submit form
selenium.Type("username", "MyUsername");
selenium.Type("password", "MyPassword");
selenium.Click("submit");
selenium.WaitForPageToLoad("1000");
// Assert Redirect
Assert.AreEqual("http://localhost/dashboard", driver.Url);
}
Should I practice dependency injection?
Dependency injection, or as it's also known, passing arguments.
— Nicholas C. Zakas
public MonthlyBilling(MemberRepositoryInterface memberRepository,
BillingInterface billing)
{
this.memberRepository = memberRepository;
this.billing = billing;
}
Again, ask yourself if the given class should be responsible for a particular task. If not, get it out of there! The smaller the class, the easier it is to test.
public void DoThatThingYouDo()
{
var chargeInfo = ['...'];
var stripe = new StripeBilling(); // gasp!
stripe.Charge(chargeInfo);
}
// better
public MonthlyBiller(BillingInterface billing)
{
this.billing = billing;
}
public void DoThatThingYouDo()
{
var chargeInfo = ['...'];
this.billing.Charge(chargeInfo); // any implementation will do
}
public class StripeBilling : BillingInterface {
public void Charge(BillingInfo info)
{
// ... bill with Stripe
}
}
public class BrainTreeBilling : BillingInterface {
public void Charge(BillingInfo info)
{
// ... bill with BrainTree
}
}
"A mock object is nothing more than a bit of test jargon that refers to simulating the behavior of real objects."
public class MonthlyBilling {
protected MemberRepositoryInterface memberRepository;
protected BillingInterface billing;
public MonthlyBilling(MemberRepositoryInterface memberRepository,
BillingInterface billing)
{
this.memberRepository = memberRepository;
this.billing = billing;
}
public void Charge()
{
var members = this.memberRepository.GetActiveMembers();
foreach (var member in members) {
this.billing.Charge(member);
}
}
}
[TestMethod]
public void TestChargesActiveMembersEachMonth()
{
// Mock Dependencies
var members = GetTestMembers();
var memberRepository = Isolate.Fake.Instance<MemberRepositoryInterface>();
Isolate.WhenCalled(() => memberRepository.GetActiveMembers()).WillReturn(members);
var billing = Isolate.Fake.Instance<BillingInterface>();
Isolate.WhenCalled(() => billing.Charge(null)).IgnoreCall();
// Call function being tested
new MonthlyBilling(memberRepository, billing).Charge();
// Verify behavior
int numberOfBillings = Isolate.Verify.GetTimesCalled(() => billing.Charge(null));
Assert.AreEqual(members.Count, numberOfBillings);
Isolate.Verify.WasCalledWithAnyArguments(() => memberRepository.GetActiveMembers());
}
[TestMethod]
public void TestChargesActiveMembersEachMonth()
{
var members = GetTestMembers();
var memberRepository = MockGetActiveMembers(members);
var billing = MockBillingCharge();
new MonthlyBilling(memberRepository, billing).Charge();
AssertMonthlyBillingBehavior();
}
"Try not to overuse mocks. Doing so paves the way for tests that are significantly more difficult to understand and maintain. Don't forget that setting expectations on a mock allows implementation details to leak into your tests."
— Jeffrey Way
Given the user does not have admin rights
When the user attempts to navigate to the admin section
Then the user is denied access
[TestMethod]
public void GivenNonAdminUser_WhenNavigatingToAdminArea_DenyAccess()
{
// This most likely mocks out user retrieval
GivenNonAdminUser();
// var result = controller.Index() as ActionResult;
NavigateToAdminArea();
// Assert.AreEqual(401, controller.Response.StatusCode);
AssertAccessDenied();
}