A blog about software and making.

Unit Testing

Notes from a .Net user group talk on unit testing.

  • Ideal Test
    • Automated
    • No complex dependencies
    • Independent of each other. No sequences!
    • Describes intention.
    • Shows you how the code should be used.
    • Written at the same time or before the code being tested. Code -> Test -> Code -> Test -> …
    • No loops, no conditionals
    • Hide any elaborate setup in a base fixture or private method.
    • Avoid complex setup & teardown (code smell!)
  • Obvious Benefits
    • Reduce time between code change and bug (test failing). You want to reduce your feedback loop.
    • Block regression bugs.
    • Fewer bugs in next phase.
  • Hidden Benefit: Exerts design pressures on code
    • Avoid the debugger! If you can’t tell what is wrong from the test maybe you need smaller more focused tests!
    • Less coupling
    • Explicit dependencies (elaborate setup is a code smell)
    • Interface use for dependencies
    • Keeping tests focused encourages single responsibility
    • Smaller classes & shorter methods
    • Enables refactoring -> Readable code
    • You can gut your classes and refactor legacy code.
  • What to test
    • public methods (use a consumer’s point of view)
    • pure functions
  • Clean tests
    • Have a standard naming scheme. Ex: Method_Scenario_Result()
    • Organise into sections Arrange / Act / Assert (TripleA Style)
    • Ideally one assert per test
    • Can you share private setup method over multiple tests?
    • Can you put shared setup in test initialization method or base class?
  • Testing styles
    • State Verification (Good) - Verify state of the object. Given these inputs, I expect these outputs.
    • Behavior Verification (Bad) - Verify calls between SUT and collaborators using mocks. Test code is coupled to code under test so it tends to be brittle and break whenever you change the code being tested.
  • Isolation
    • Avoid “new”
    • Dependancy injection (Good)
      • Create collaborators outside of SUT and pass in (usually via constructor).
      • Service container IOC usually used to create dependencies in code.
    • Service Location (Bad)
      • Have to setup global state of singleton factory for each test.
      • Injecting the container into a parameter and then using it to new up is still a service locator.
    • Interception Frameworks (Bad)
      • Invasive testing that leads to test code that is tightly coupled to code under test.
      • You lose the benefits of design pressure encouraging interface use and small classes.
      • “Pointy tool” - Use with extreme caution
  • Service Containers (IOC container)
    • Controls lifecycle of its objects. Not just a service locator.
    • Lazy - The first instance is new and the rest are copies.
    • Being forced to write to interfaces is a design pressure that forces decoupling.
  • Test Doubles
    • Stub (Good) - Just works with minimal behavior. Null and real objects with fake data are stubs.
    • Mock (Meh) - Can set expectations and can tell you when they aren’t met.
    • Fake (Meh) - Behaves like the real thing (more or less). Created by hand.
    • Spy (Bad) - Tells you what happened by recording calls and checking what SUT did. Very brittle.
  • Mocks design pressure
    • Easier to check edge cases
    • Wrap static methods
    • Composed - Pressure away from deep inheritance
    • Uses targeted interfaces
    • Considers requests. What kind of supported classes are needed?
    • One object many interfaces
    • Start coding anywhere! Mock out what hasn’t been coded yet.
  • Mocking pros
    • Can record expected calls
    • Have interface of expected object
    • Can return expected call values.
    • Can verify calls have been made.
    • Fluent API
  • Mocking cons
    • Premature composition
    • Premature interface injection

Odds & Ends - March 2013

Thoughts, terms, and ideas I’ve come across over the last few months.

  • Lines of code represent an expenditure, not an accomplishment.
  • In large systems interfaces matter. There is a danger that people will start playing fast and loose with interfaces by passing around nested maps and lists and then expecting the other side to understand the encoding. Be explicit!
  • In ECMAScript one should use an object not an array for a set of key/value pairs. If you use an array all you are getting is the extra baggage of an array’s methods what won’t work.
  • Functions without side-effects are pure data transformations. Something comes in and something comes out. The results are predictable. Functions that rely on side effects or additional inputs (databases) are complicated.
  • Doing proper single responsibility principal often results in a class with one method … aka a function.
  • Collections vs. Streams
    • Collection - (eager, a full warehouse) data structure which holds all values in memory. Each element must be computed before it can be added.
      • Values are spread out in space (computer memory) (which all exist at one point in time).
    • Streams - (lazy, just in time) data structure where elements are computed on demand. Liked producer/consumer or lazy version of a collection.
      • Values are spread out in time (which repeatedly appear at the same point.
  • User interaction -> data change -> view render
    • Render on the data change, not the user interaction! The model is a single source of truth!
  • Layers Pattern
    1. UI - Views and models
    2. Controller - Application composition
    3. Service Layer - Business composition and coordination.
    4. Repositories (data access), Calculators (algorithms), and Query Objects (helpers).
  • C# Anonymous functions
    • Can declare a variable with the same name as static/instance variable of the outer class.
    • Can access static/instance variables of enclosing class
    • Cannot access any ref/out variables inside an anonymous method.
    • Cannot declare a variable with the same name as a variable in the outer method.
  • Closure - Is not a concrete element or synonym for an anonymous function or lambda expression! It’s a behavior that allows us to declare an anonymous function that refers to local variables outside said function and not worry about that variables lifespan.
  • Abstract Base Class vs. Interface
    • Abstract Base Class - “Is a”
      • Ex: Dog is a mammal
      • This is core functionality of this class
      • Adds constraint that there can be only one base class
    • Interface - “acts like”, “is verb”
      • Ex: Book is IWritable, Dog acts like an IEater
      • This is trait of an object, not necessarily its core functionality
  • The key principal of HTTP is that resources are sent in the message body, using content negotiation to specify the representation of the resource.
  • In C++ ptr[3] means (ptr + 3) which can be written as (3 + ptr) or 3[ptr] :-)
  • To reduce stalls due to the GC we circumvented it by replacing all heap allocations with the use of pre-allocated arrays of values types, effectively implementing manual memory management inside a managed language.

Functional vs. Imperative Environments

What is the primary building block of your programs?

  • Functional

    • Mostly functional with imperative where needed. Managing what state is intrinsic, necessary, and appropriate.
    • The Basic building block is a referentially transparent function. A function which returns the same output per given input every time.
    • The environment does not affect the output of a referentially transparent function so we can reason about it independently.
  • Imperative

    • The Basic building block is a stateful action (object in OO).
    • Without environmental knowledge it’s impossible to reason about and test.
    • You must be able to control the environment in which the action occurs to know if it’s being done right.
    • The environment is an implicit parameter on the action. Making the environment explicit makes it referentially transparent.

SortedList vs. SortedDictionary

Both are useful if you need to iterate over elements in sorted order. As a rule of thumb if you creating it once and not modifying it than a SortedList has a faster construction time and uses less memory than a SortedDictionary.

Operation SortedList SortedDictionary
retrieval by key O(log(n)) O(log(n))
insert and remove elements O(n) O(log(n))
contains key O(log(n)) O(log(n))

Sorted Dictionary

  • Implemented as binary search tree for fast lookups while keeping elements in sorted order.

Sorted List

  • Implemented as two arrays (keys, values) kept in sorted order.
  • Faster construction time when being populated in one go.