loopt unit test experiences

26
Unit Testing for startups Experiences from Loopt

Upload: heine-frifeldt

Post on 02-Jul-2015

1.457 views

Category:

Technology


0 download

DESCRIPTION

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.

TRANSCRIPT

Page 1: Loopt unit test experiences

Unit Testing for startups

Experiences from Loopt

Page 2: Loopt unit test experiences

Heine Frifeldt <[email protected]>

• Server Team Manager

• Got Introduced to extreme programming in

2000 @ Adomo DK• Have previously tried to have lots of manual tests

• Realized the value of unit tests

• Continued Agile Development in Adomo US

• Joined Loopt in 2008• Code examples are in C#, but topics should apply

regardless of language

• Feel free to ask questions

2

Page 3: Loopt unit test experiences

Loopt – What do we do?

3

Connecting You with Friends and Family

Page 4: Loopt unit test experiences

Loopt – What do we do?

4

Connecting You with the Places You Go

Page 5: Loopt unit test experiences

Loopt – What do we do?

5

Connecting You with Your Local Businesses

Page 6: Loopt unit test experiences

Time for Unit Test in Startup?

• Turn one-off tests into automated tests

• Takes extra time upfront but it’s worth it

• Legacy clients likely for mobile companies

• Confident deployments

• Unit tests work as documentation

• Easy way for new employees to get

familiarized with code

6

Page 7: Loopt unit test experiences

Overview

• Initial state of Loopt tests

• Untestable code

• Code structure

• Improved code

• Lessons learned

• Tools

7

Page 8: Loopt unit test experiences

Initial Experience

• My first UT experiences we wrote the tests

along with code• You structure your code for testability

• Adding tests for existing code can be much

harder

• Believe common startup problem• You get to a point where you realize it would be nice

with unit tests

8

Page 9: Loopt unit test experiences

Unit Tests at Loopt

• General support behind Unit Test• Most eng. wanted to add unit tests, but it was hard, so in

practice new tests were rarely added

• VPE had previous good experiences with unit tests and

our deployment did not have good track record

• The existing tests were neglected

• One test project for all projects

• Few tests

• End to end functional tests

• Complex architecture and code that requires mobile to

invoke

9

Page 10: Loopt unit test experiences

Code Example (Database Access Layer)

• One of my first tasks was to add a LockedOut

property to our User class

• In theory to test it, Initialize LooptUser, Emulate

failed login N times, Verify field got set

• In practice• Constructor takes phone number (or session, or …) which reads

and sets all relevant properties directly from DB

• Login is a static method in a different class which makes direct

DB calls

• Checks your phone make/model

• Makes external billing checks to certain carriers

10

Page 11: Loopt unit test experiences

Code Example (Business Logic)

• Loopt Cell Server takes binary stream

from clients and processes request

• Stream passed down through the flow –

and maybe modified

• Some tests used the raw binary stream to

verify functionality

11

Page 12: Loopt unit test experiences

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

Page 13: Loopt unit test experiences

Code Example (Static Initializers)

• Carrier class has internal constructor

• Get it from carrier factory• 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

Page 14: Loopt unit test experiences

Argh!!

• Ways of testing suggested to me

• LooptUser - Create a test account and use number and

password in tests

• CellServer - Manual test with real phone

• Carrier - Use phone number of wanted carrier

• Manually create test data

• Didn’t know what to change and where to begin

and end

14

Page 15: Loopt unit test experiences

Testable Code Talk

• Hosted unit test talks about testable code• Misko Hevery @ Google

• http://misko.hevery.com/2008/11/11/clean-code-talks-

dependency-injection

• Key points• Avoid use of statics (your own and base class libraries)

• Keep constructors simple - they cannot be overridden.

• Separate object graph from logic – remove new operators

• Ask for what you need (dependency injection) and have

DI framework to initialize root objects

• Hollywood Principle

15

Page 16: Loopt unit test experiences

Improvements

• Immediate steps (no tools)• Start using dependency injection in new code

• Refactor existing code when changes are required

• Use our own BuildObject initializers

• Use our own mock object implementations for testing

• After ~6 months

• Core code was more nicely structured

• Unit tests and root objects were cluttering up with object

graph initializations

• Starting using Ninject

• After ~2 years

• Got Moq demoed. Just started using that.

16

Page 17: Loopt unit test experiences

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

Page 18: Loopt unit test experiences

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

Page 19: Loopt unit test experiences

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

Page 20: Loopt unit test experiences

Lessons Learned - Test Barriers

• Hook up Ninject immediately in new projects

• New operators can quickly creep back in

• Add helpers when “impossible” or impractical to

change

• Use IDispose interface for TransientXXXXX test classes

• Users

• Phone numbers

• Carriers

• Config files

• Non-ideal tests are better than no tests

• Well, usually ;-)

20

Page 21: Loopt unit test experiences

Lessons Learned – Adding Tests

• No strict test requirements; test can be written up

front or after

• Tests added later are better than no tests

• Add tests when you encounter a bug

• Bookmark/revisit hard to test code and set goal to

write one test

• Restructure / helpers will result in many more tests

• Tools can be introduced gradually

• Introduce DI from top to bottom to avoid cascading

changes

21

Page 22: Loopt unit test experiences

Current Tools

• Source Control - Mercurial

• Unit Test Framework – Visual Studio

• Automated Build Environment - Jenkins

• Code style – Dependency Injection

• Dependency Injection Framework - Ninject

• Mock Framework - Moq

22

Page 23: Loopt unit test experiences

State of our tests

• 34 Test Projects• 1008 Unit Tests. Executed on commit. Takes ~10 min

• 23 Functional tests. Executed nightly. Takes ~1 min

• Too long execution time

• Flaky tests tend to put us into bad streaks

of several days with failing tests• Due to timing

• Due to functional test nature

23

Page 24: Loopt unit test experiences

Jenkins (aka Hudson)

• Nice overview of projects of their state

• Age of failing unit tests

• Execution time of unit tests

24

Page 25: Loopt unit test experiences

Next steps

• Continue refactoring existing code to use

DI/Ninject

• Refactor existing tests to be more unit and less

functional test• Faster

• Not flaky

• Better process for dealing with broken tests in

crunch mode• Don’t want to ignore false negatives

• Don’t notice new failures

• Wait for better Ninject integration with ASP.NET,

MVC, WCF, NT Services.

25

Page 26: Loopt unit test experiences

We are hiring ($5000 referral bonus)

• System Administrator

• Build Engineer

• Metrics Engineer

• Multiple QA

• Server

• iPhone

• Android

• Web

26