Loopt unit test experiences

  • 1,416 views
Uploaded on

We all hear how unit tests can ensure higher quality code and help us in day to day refactoring, but is it feasible to write and maintain unit tests in a fast paced startup company? …

We all hear how unit tests can ensure higher quality code and help us in day to day refactoring, but is it feasible to write and maintain unit tests in a fast paced startup company?

This is a presentation by server lead, Heine Frifeldt, on how unit tests was gradually introduced into the server code base at Loopt, which tools are used in the continuous build environment, coding techniques and lessons learned.

More in: Technology , Education
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
    Be the first to like this
No Downloads

Views

Total Views
1,416
On Slideshare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
7
Comments
0
Likes
0

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide
  • No constructorStatic method to execute logicMakes connection to DB and invokes stored procReads from binary stream and converts to storecproc arguments
  • Notice ConfigurationManager.GetSection is a static from BCLSome code paths would check “IsProduction” config setting and behave according to environment

Transcript

  • 1. Unit Testing for startups
    Experiences from Loopt
  • 2. Heine Frifeldt <heine@loopt.com>
    • Server Team Manager
    • 3. Got Introduced to extreme programming in 2000 @ Adomo DK
    • 4. Have previously tried to have lots of manual tests
    • 5. Realized the value of unit tests
    • 6. Continued Agile Development in Adomo US
    • 7. Joined Loopt in 2008
    • 8. Code examples are in C#, but topics should apply regardless of language
    • 9. Feel free to ask questions
    2
  • 10. Loopt – What do we do?
    3
    Connecting You with Friends and Family
  • 11. Loopt – What do we do?
    4
    Connecting You with the Places You Go
  • 12. Loopt – What do we do?
    5
    Connecting You with Your Local Businesses
  • 13. Time for Unit Test in Startup?
    • Turn one-off tests into automated tests
    • 14. Takes extra time upfront but it’s worth it
    • 15. Legacy clients likely for mobile companies
    • 16. Confident deployments
    • 17. Unit tests work as documentation
    • 18. Easy way for new employees to get familiarized with code
    6
  • 19. Overview
    7
  • 25. Initial Experience
    • My first UT experiences we wrote the tests along with code
    • 26. You structure your code for testability
    • 27. Adding tests for existing code can be much harder
    • 28. Believe common startup problem
    • 29. You get to a point where you realize it would be nice with unit tests
    8
  • 30. Unit Tests at Loopt
    • General support behind Unit Test
    • 31. Most eng. wanted to add unit tests, but it was hard, so in practice new tests were rarely added
    • 32. VPE had previous good experiences with unit tests and our deployment did not have good track record
    • 33. The existing tests were neglected
    • 34. One test project for all projects
    • 35. Few tests
    • 36. End to end functional tests
    • 37. Complex architecture and code that requires mobile to invoke
    9
  • 38. Code Example (Database Access Layer)
    • One of my first tasks was to add a LockedOut property to our User class
    • 39. In theory to test it, Initialize LooptUser, Emulate failed login N times, Verify field got set
    • 40. In practice
    • 41. Constructor takes phone number (or session, or …) which reads and sets all relevant properties directly from DB
    • 42. Login is a static method in a different class which makes direct DB calls
    • 43. Checks your phone make/model
    • 44. Makes external billing checks to certain carriers
    10
  • 45. Code Example (Business Logic)
    • Loopt Cell Server takes binary stream from clients and processes request
    • 46. Stream passed down through the flow – and maybe modified
    • 47. Some tests used the raw binary stream to verify functionality
    11
  • 48. Code Example (Business Logic)
    public class DeleteJournal{
    private DeleteJournal() {}
    public static void ProcessRequest(byte[] content)
    {
    using (SqlData sdp = new SqlData("sp_Delete_Journal"))
    {
    byte deltype = content[pos++];
    byte num = content[pos++];
    entry = BitUtil.ReadInt(content, ref pos);
    sdp.AddParameter("@EntryID", SqlDbType.Int, entry);
    […]
    12
  • 49. Code Example (Static Initializers)
    • Carrier class has internal constructor
    • 50. Get it from carrier factory
    • 51. Has static initializer which reads carrier settings from a config file
    static CarrierFactory()
    {
    SmppMap = new Dictionary<string, SmppConnectionElement>();
    CarrierSettings configSection = 
    (CarrierSettings)ConfigurationManager.GetSection("carriers");
    foreach (SmppElemtn sce in configSection.SmppConnectionElements)
    {
    […]
    13
  • 52. Argh!!
    • Ways of testing suggested to me
    • 53. LooptUser - Create a test account and use number and password in tests
    • 54. CellServer - Manual test with real phone
    • 55. Carrier - Use phone number of wanted carrier
    • 56. Manually create test data
    • 57. Didn’t know what to change and where to begin and end
    14
  • 58. Testable Code Talk
    • Hosted unit test talks about testable code
    • 59. MiskoHevery@ Google
    • 60. http://misko.hevery.com/2008/11/11/clean-code-talks-dependency-injection
    • 61. Key points
    • 62. Avoid use of statics (your own and base class libraries)
    • 63. Keep constructors simple - they cannot be overridden.
    • 64. Separate object graph from logic – remove new operators
    • 65. Ask for what you need (dependency injection) and have DI framework to initialize root objects
    • 66. Hollywood Principle
    15
  • 67. Improvements
    • Immediate steps (no tools)
    • 68. Start using dependency injection in new code
    • 69. Refactor existing code when changes are required
    • 70. Use our own BuildObject initializers
    • 71. Use our own mock object implementations for testing
    • 72. After ~6 months
    • 73. Core code was more nicely structured
    • 74. Unit tests and root objects were cluttering up with object graph initializations
    • 75. Starting using Ninject
    • 76. After ~2 years
    • 77. Got Moq demoed. Just started using that.
    16
  • 78. Example Class using Dependency Injection
    public class GrouponAdapter : IGrouponAdapter
    {
    private readonly IPoiController _poiController;
    private readonly DataContextProvider _contextProvider;
    private readonly ILooptWebClient _looptWebClient;
    [Inject]
    public GrouponAdapter(IPoiController poiController, DataContextProvider contextProvider,  ILooptWebClient looptWebClient)
    {
    _poiController = poiController; _contextProvider = contextProvider; _looptWebClient = looptWebClient;
    }

    17
  • 79. Example Ninject Bindings
    public class LooptLogicModule : NinjectModule
    {
    public override void Load()
    {
    Bind<IPoiController>().To<PoiController>().InSingletonScope(); Bind<ILooptWebClient>().To<LooptWebClient>().InSingletonScope();

    private IKernel _kernel;
    _kernel.Get<IGrouponAdapter>();
    18
  • 80. Example Unit Test using Moq
    [TestMethod]
    public void ParseGrouponDeals()
    {
    // Results in 6 deals being returned.
    var webClient = new Mock<ILooptWebClient>();
    webClient.Setup(c => c.DownloadString(It.IsAny<Uri>())).
    Returns(Resources.Groupon_Deals_In_Austin);
    IGrouponAdapter ga = new GrouponAdapter(null, webClient.Object);
    Deal[] deals = ga.GrouponDeals(new Coordinate(30.44595, -97.79016));
    Assert.AreEqual(6, deals.Length, "Expected 6 deals");
    }
     
    19
  • 81. Lessons Learned - Test Barriers
    • Hook up Ninject immediately in new projects
    • 82. New operators can quickly creep back in
    • 83. Add helpers when “impossible” or impractical to change
    • 84. Use IDispose interface for TransientXXXXX test classes
    • 85. Users
    • 86. Phone numbers
    • 87. Carriers
    • 88. Config files
    • 89. Non-ideal tests are better than no tests
    • 90. Well, usually ;-)
    20
  • 91. Lessons Learned – Adding Tests
    • No strict test requirements; test can be written up front or after
    • 92. Tests added later are better than no tests
    • 93. Add tests when you encounter a bug
    • 94. Bookmark/revisit hard to test code and set goal to write one test
    • 95. Restructure / helpers will result in many more tests
    • 96. Tools can be introduced gradually
    • 97. Introduce DI from top to bottom to avoid cascading changes
    21
  • 98. Current Tools
    • Source Control - Mercurial
    • 99. Unit Test Framework – Visual Studio
    • 100. Automated Build Environment - Jenkins
    • 101. Code style – Dependency Injection
    • 102. Dependency Injection Framework - Ninject
    • 103. Mock Framework - Moq
    22
  • 104. State of our tests
    • 34 Test Projects
    • 105. 1008 Unit Tests. Executed on commit. Takes ~10 min
    • 106. 23 Functional tests. Executed nightly. Takes ~1 min
    • 107. Too long execution time
    • 108. Flaky tests tend to put us into bad streaks of several days with failing tests
    • 109. Due to timing
    • 110. Due to functional test nature
    23
  • 111. Jenkins (aka Hudson)
    • Nice overview of projects of their state
    • 112. Age of failing unit tests
    • 113. Execution time of unit tests
    24
  • 114. Next steps
    • Continue refactoring existing code to use DI/Ninject
    • 115. Refactor existing tests to be more unit and less functional test
    • 116. Faster
    • 117. Not flaky
    • 118. Better process for dealing with broken tests in crunch mode
    • 119. Don’t want to ignore false negatives
    • 120. Don’t notice new failures
    • 121. Wait for better Ninject integration with ASP.NET, MVC, WCF, NT Services.
    25
  • 122. We are hiring ($5000 referral bonus)
    26