Using xUnit as a Swiss-Army Testing Toolkit (Does Unit Size Matter?) ACCU Conference 2011 Chris Oldwood
Stream of Consciousness Developer Driven Testing The Essence of (x)Unit Testing Those Pesky Dependencies Code & Test Evolution in Practice
Stream of Consciousness Developer Driven Testing The Essence of (x)Unit Testing Those Pesky Dependencies Code & Test Evolution in Practice
Text Book Test string[][] tests = { { "3", "4", "+", "7" }, { "9", "1", "-", "8" }, { "2", "3", "*", "6" }, { "9", "3", "/", "3" }, }; void run_tests() { var calculator = new Calculator(); foreach(var test in tests) { var lhs = test[0]; var rhs = test[1]; var op = test[2]; var result = calculator(lhs, rhs, op); assert(result == test[3]); }
Exercise Left for the Reader External System 1External System 2External System 3 The System 42 Services Database
Unit Integration System Component Stress Lexicon of Testing End-to-End Regression White Box Black Box Characterisation Exploration
System Integration Component Unit Dependencies Feedback Unit Evolution All Regression
Stream of Consciousness Developer Driven Testing The Essence of (x)Unit Testing Those Pesky Dependencies Code & Test Evolution in Practice
Test == Specification public void Execute_Should_Elide_Agreement_When_No_Trades_Match() { var trades = new List { new Trade("trade-id", "product-a") }; var agreement = new Agreement("product-b"); var task = new PreparationTask(trades, agreement); var result = task.Execute(s_services); Assert.That(task.Agreement, Is.Null); }
Consistent Style public void a_c_sharp_test() { var arrangement = new Arrangement(); var result = arrangement.action(); Assert.That(result, Is.EqualTo(expectation)); } create procedure a_sql_test as declare arrangement varchar(100), result varchar(100) exec = = result exec "expectation" go
Minimises Dependencies MyService External ServiceDatabaseFile System Mock External Service IExternalServiceIFileSystemIDatabase Mock File System Mock Database
Promotes Arbitrary Code Execution public void Prepare_Should_Elide_Agreement_When_No_Trades_Match() { var trades = new List { new Trade("trade-id", "product-a") }; var agreement = new Agreement("product-b"); var task = new PreparationTask(trades, agreement); var result = task.Execute(s_services); Assert.That(task.Agreement, Is.Null); } LibraryEXE Stub Test RunnerLibraryTestsDebugger Custom Test Harness
Automated Testing Lowers the barrier to running tests Regression testing is implicit Build server watches your back
Stream of Consciousness Developer Driven Testing The Essence of (x)Unit Testing Those Pesky Dependencies Code & Test Evolution in Practice
Pesky Dependencies External System 1External System 2External System 3 The System Service 2 Service 1 File-System Database
xUnit Abuse Fight the Shadow Cache Invoke TearDown from SetUp Test/build failure isnt absolute
File-System (Reading) Source Control directory Build server directory Resource files
File-System (Writing) TEMP directory Output directory
Database Per-user / per-branch workspace Only need schema not data (Integration) Can reuse existing unit test database Use same code revision for compatibility Use transactions to avoid residual effects Fake tables with CSV files
Database Asserts public void AddCustomer_Should_Persist_The_Customer() { const id = 1234; const name = "name"; var customer = new Customer(...); using (var connection = AcquireConnection()) { CustomerDataMapper.AddCustomer(customer, connection); Assert.That(RowExists("dbo.Customer", " CustomerId = {0}" + " AND CustomerName = '{1}'", id, name), Is.True); }
Database SetUp/TearDown [TestFixture, TestCategory.DatabaseTest] public class SomeEntityTests : DatabaseTestBase { [TestFixtureSetUp] public void FixtureSetUp { using(var connection = AcquireConnection()) { connection.Execute("insert into thingy_table values(1, 2, 3)"); connection.Execute("test.InsertThingy(1, 2, 3)"); } [TestFixtureTearDown] public void FixtureTearDown { using(var connection = AcquireConnection()) { connection.Execute("delete from thingy_table"); connection.Execute("test.DeleteAllThingys"); }
Helper Base Class public class DatabaseTestBase { public ISqlConnection AcquireConnection() { return... }... public bool RowExists(string table, string where, string params[]) { string filter = String.Format(where, params); string sql = String.Format( "select count(*) as [Count] from {0} where {1}", table, filter); using (var connection = AcquireConnection()) { var reader = connection.ExecuteQuery(sql); return (reader.GetInt("Count") == 1); }... }
External Systems Verify API behaviour Test internal façade Reliability varies (DEV vs PROD)
Stream of Consciousness Developer Driven Testing The Essence of (x)Unit Testing Those Pesky Dependencies Code & Test Evolution in Practice
System Architecture Market DataTrade DataAnalytics The System 42 Services Database
Initial System Test Market Data Service Trade Data Service Analytics Service Calculator Test Runner System Tests [Test, TestCategory.SystemTest] public void Calculate_Answer() {... var result = c.calculate(); Assert.Equal(result, 42); }
Addressing External Risks External Market Data Service API External Trade Data Service API External Market Data Service Tests External Trade Data Service Tests Test Runner
Internal Service Design External Service API External Service Tests Internal Service External Service Facade Internal Service Tests Mock External Services Performance Test Runner Mock Service
Data Access Layer Database Public Interface Database Unit Tests Data Access Layer Data Access Layer Tests Database API Mock Database API Mock Data Access Layer
Database Public Interface External Analytics Service External Market Data Service API External Market Data Service API System Evolution Mock Market Data Service Mock Trade Data Service Mock Analytics Service Calculator Test Runner Unit / Integration / System Tests [Test, TestCategory.SystemTest] public void Calc_Answer_For_ABC_Plc() {... var result = c.calculate(); Assert.Equal(result, 41.75); } Mock Data Access Layer Market Data Service Trade Data Service Analytics Service Data Access Layer
The Oldwood Thing Chris Oldwood