public class House
{
private Kitchen kitchen = new Kitchen();
private Bedroom bedroom;
public House()
{
this.bedroom = new Bedroom();
}
}
public class House
{
private Kitchen kitchen;
private Bedroom bedroom;
@Inject
public House(Kitchen k, Bedroom b)
{
this.kitchen = k;
this.bedroom = b;
}
}
public void TestThisIsReallyHard()
{
House house = new House();
// Darn! I'm stuck with those Kitchen and
// Bedroom objects created in the
// constructor...
}
public void testThisIsEasyAndFlexible()
{
Kitchen dummyKitchen = new DummyKitchen();
Bedroom dummyBedroom = new DummyBedroom();
House house =
new House(dummyKitchen, dummyBedroom);
// Awesome, I can use test doubles that
// are lighter weight...
}
public class AccountView
{
private User user;
public AccountView()
{
// Static methods are non-interceptable,
// and non-mockable
this.user = DatabaseManager.GetCurrentUser();
}
}
public class AccountView
{
private User user;
public AccountView(User user)
{
this.user = user;
}
}
// Hard to test because needs real database
public void TestWithRealDatabase()
{
AccountView view = new AccountView();
// Dammit! We just had to connect to a
// real database. This test is now slow.
}
// Easy to test with dependency injection
public void TestLightweightAndFlexible()
{
User user = new DummyUser();
AccountView view = new AccountView(user);
// Easy to test and fast with test-double user
}
public class TaxCalculator {
private TaxTable taxTable;
public SalesTaxCalculator(TaxTable taxTable) {
this.taxTable = taxTable;
}
float ComputeSalesTax(User user, Invoice invoice) {
// Note that "user" is never used directly
Address address = user.Address;
float amount = invoice.SubTotal;
return amount * taxTable.GetTaxRate(address);
}
}
public class TaxCalculator {
private TaxTable taxTable;
public SalesTaxCalculator(TaxTable taxTable) {
this.taxTable = taxTable;
}
float ComputeSalesTax(Address address, float amount)
{
// No user, nor digging inside the invoice
return amount * taxTable.GetTaxRate(address);
}
}
void PainfulTaxCalculatorTest()
{
TaxCalculator calc = new TaxCalculator(new TaxTable());
// Testing exposes the problem by the amount
// of work necessary to build the object graph
Address address = new Address("1600 Amphitheatre Parkway...");
User user = new User(address);
Invoice invoice = new Invoice(1, new ProductX(95.00));
Assert.AreEqual(0.09,
calc.ComputeSalesTax(user, invoice));
}
void QuickTaxCalculatorTest()
{
TaxCalculator calc = new TaxCalculator(new TaxTable());
// Only wire together the objects that are needed
Address address = new Address("1600 Amphitheatre Parkway...");
Assert.AreEqual(0.09,
calc.ComputeSalesTax(address, 95.00));
}
public class MembershipPlan
{
public void ProcessOrder(UserContext userContext)
{
User user = userContext.User;
PlanLevel level = userContext.Level;
Order order = userContext.Order;
// Do stuff
}
}
public class MembershipPlan
{
public void ProcessOrder(User user,
PlanLevel level, Order order)
{
// Do stuff
}
}
public void TestWithContext()
{
UserContext userContext = new UserContext
{
User = new User("Kim"),
Order = new Order("SuperDeluxe", 100, true),
Level = new PlanLevel(143, "yearly")
}
MembershipPlan plan = new MembershipPlan();
plan.processOrder(userContext);
// Then make assertions against the user, etc ...
}
public void TestWithApiDeclaringWhatItNeeds()
{
User user = new User("Kim");
PlanLevel level = new PlanLevel(143, "yearly");
Order order = new Order("SuperDeluxe", 100, true);
MembershipPlan plan = new MembershipPlan();
plan.processOrder(user, level, order);
// Then make assertions against the user, etc ...
}
/// <summary>
/// Checks if a mailbox has any error.
/// </summary>
/// <param name="mailbox">The mailbox.</param>
/// <returns>True if has errors.</returns>
public static bool HasErrors(Mailbox mailbox)
{
// Data validation
Errors.Assert(null != mailbox);
Errors.Assert(Guid.Empty != mailbox.Id);
// Get the ticket
Ticket ticket = new Ticket(WebAuthUtils.User.Id, false);
// Get migration stats
MailboxStat mailboxStat = Mailbox.GetMigrationStat(mailbox.Id);
// Return true if there are too many errors: more than 20 and more than 3%
const int minErrors = 20;
const double minErrorPercentage = 0.03;
return (minErrors < mailboxStat.TotalErrorCount)
&& (minErrorPercentage * mailboxStat.TotalErrorCount < mailboxStat.TotalErrorCount);
}
public class TheSurroundingClass
{
public IDataManager DataManager { get; set; }
public TheSurroundingClass(IDataManager dataManager)
{
this.DataManager = dataManager;
}
public bool HasErrors(Mailbox mailbox)
{
// Data validation
Errors.Assert(null != mailbox);
Errors.Assert(Guid.Empty != mailbox.Id);
// Get the ticket
Ticket ticket = new Ticket(WebAuthUtils.User.Id, false);
// Get migration stats
MailboxStat mailboxStat = this.DataManager.Find("MailboxStat", mailbox.Id);
// etc.
}
}
public interface IDataManager
{
public Entity Find(string entityName, Guid id);
}
public class SqlDataManager
{
public Entity Find(string entityName, Guid id)
{
// Do some SQL crap
}
}
public class FakeDataManager
{
public Entity Find(string entityName, Guid id)
{
// Return in-memory fake object
}
}
// Create a new Data Manager mock
Mock<IDataManager> dataManagerMock = new Mock<IDataManager>();
// Create a fake mailbox stat object
MailboxStat mailboxStat = new MailboxStat() { Speed = 12, Foo = "Bar" };
// Setup the mock to return fake mailbox stat (version 1)
dataManagerMock.Setup(p => p.Find(It.IsAny<string>(), It.IsAny<Guid>())).Returns(mailboxStat);
// Setup the mock to return fake mailbox stat (version 2)
dataManagerMock.Setup(p => p.Find("MailboxStat", It.IsAny<Guid>())).Returns(mailboxStat);
public interface IDataManager
{
public Entity Find(string entityName, Guid id);
}
public class SqlDataManager
{
public Entity Find(string entityName, Guid id)
{
// Do some SQL crap
}
}
public class FooContext : IFooContext
{
private FooContextState state;
private ReaderWriterLockSlim theLock;
private FooContextDescriptor internalObject;
private ReaderWriterLockSlim anotherLock;
public BarManagement BarManager { get; private set; }
public OtherManagement OtherManager { get; private set; }
internal FooContext(FooContextType fooType, Guid userId)
{
this.internalObject = FooContextUtility.CreateFooDescriptor(fooType, userId);
this.Initialize();
}
private void Initialize()
{
this.theLock = new ReaderWriterLockSlim();
this.BarManager = new BarManagement(this);
this.OtherManager = new OtherManagement(this);
this.PlopManager = new PlopManagement(this);
this.EvenMoreManager = new EvenMoreManagement(this);
this.anotherLock = new ReaderWriterLockSlim();
this.state = this.internalObject.State;
}
}
// Hard to test
public static long DetermineSomething(Guid licensePackId)
{
// Get the license pack
LicensePack licensePack = LicensePack.Retrieve(licensePackId);
Errors.AssertNotNull(licensePack);
// Return 0 if blah
if (licensePack.Blah)
return 0;
// Get invoice
Guid invoiceId = licensePack.InvoiceId;
Invoice invoice = Invoice.Retrieve(invoiceId);
// Do more stuff
}
// Easier to test
public static long DetermineSomething(LicensePack licensePack, Invoice invoice)
{
// Return 0 if blah
if (licensePack.Blah)
return 0;
// Do more stuff
}
public MigrationManager(int maxApiCallRetryCount = 2)
{
// Set members
this.WebService = new WebService
{
Url = ConfigurationManager.Read<string>("WebApiUrl").Value
};
this.MaxApiCallRetryCount = maxApiCallRetryCount;
}
Text
Text
Refund Credit Card - Call Graph