Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Becoming a SharePoint Ninja

neraath
March 02, 2013

Becoming a SharePoint Ninja

SharePoint has made developing in an agile environment notoriously difficult. Specifically, test-driven and behavior-driven development have been avoided due to the complexity of the API and the environmental requirements. Yet, numerous paths exist for SharePoint developers to pursue in order to apply these high value practices to their projects. In this session, we'll discuss how Microsoft's Fakes framework will enable your team to begin unit testing. However, we'll spend most of our time going over architectural patterns that separate your business logic from SharePoint APIs, making it much easier to unit test your code, and enable your team to become test- and behavior-driven. We'll conclude with some common agile practices that are still relevant to SharePoint developers to keep their velocity high.

neraath

March 02, 2013
Tweet

More Decks by neraath

Other Decks in Programming

Transcript

  1. Your Guide: Chris Weldon • Fightin’ Texas Aggie • Microsoft

    MVP • .Net and SharePoint Developer • UNIX and Windows Sysadmin • Sr. Consultant at Improving Enterprises • [email protected]
  2. Overview • Learn value of agile testing techniques • Address

    difficulties of testing logic that leverages SharePoint • Moles Fakes Framework • Design patterns to improve testability •Code Heavy
  3. • Every commit (to the trunk) should be built •

    Keep the build fast • Notify (and make visible to) the entire team • Intrusively notify the team of failures • Make sure to fix the build after it’s broken “Fail Fast”
  4. Standard Testing Practices A Common Problem namespace SharePointLogic { public

    class ClassWithDependencies { private SqlDatabase database; public ClassWithDependencies() { this.database = new SqlDatabase(); } public INode GetNodeByName(string name) { return this.database.Where(x => x.Name.Equals(name)) .Select(); } } }
  5. • What if no database locally? • Did you write

    test logic to rebuild the database before each test run? • What if this dependency is a physical piece of hardware? Standard Testing Practices A Common Problem
  6. Standard Testing Practices How to Solve namespace SharePointLogic { public

    class ClassWithDependencies { private IDataRepository repository; public ClassWithDependencies(IDataRepository repo) { this.repository = repo; } public INode GetNodeByName(string name) { return this.repository.Where(x => x.Name.Equals(name)) .Select(); } } }
  7. Standard Testing Practices How to Solve namespace SharePointLogicTests { [TestClass]

    public class ClassWithDependenciesTests { [TestMethod] public void TestGetNodesByName() { // Arrange. INode node = new SharePointNode(); IDataRepository repository = (IDataRepository)MockRepository.Stub<IDataRepository>(); repository.Stub(x => x.Where).Returns(repository); repository.Stub(x => x.Select).Returns(node); repository.Replay(); // ...
  8. // Act. ClassWithDependencies testClass = new ClassWithDependencies(repository); INode testNode =

    testClass.GetNodeByName("Test"); // Assert. Assert.AreEqual(node, testNode); } } } Standard Testing Practices How to Solve
  9. • Most of SharePoint object model has NO interfaces •

    Worse, most also are Sealed classes, meaning no extending and overriding the SharePoint behavior • Most SharePoint objects require active connection and instance of SharePoint on local server • Unlike database projects, resetting and recreating state in SharePoint is way more difficult Standard Testing Practices What about SharePoint?
  10. Standard Testing Practices A SharePoint Common Problem public string GetNameOfSharePointWebSite(string

    url) { using (SPSite site = new SPSite(url)) { using (SPWeb web = site.OpenWeb()) { return web.Name; } } }
  11. A Trickier Problem public IList<SPUser> GetUsersInSiteCollection(string siteUrl) { using (SPSite

    site = new SPSite(siteUrl) { site.CatchAccessDeinedError = false; using (SPWeb web = site.RootWeb) { try { if (web.DoesUserHavePermissions(SPBasePermissions.ManageWeb)) { SPUsercollection users = web.Users; List<SPUser> usersWithAccess = new List<SPUser>(); foreach (SPUser user in users) { if (web.DoesUserHavePermissions(user.LoginName, SPBasePermissions.ManageWeb)) { usersWithAccess.Add(user); } } return usersWithAccess; } } catch (UnauthorizedAccessException e) { return new List<SPUser>(); } } } }
  12. How to Test: Fakes • Fakes is, simply put, a

    mocking and stubbing framework • Different than other traditional mocking frameworks • Uses detours to custom delegates via runtime instrumentation
  13. Fakes • As your dependency on SharePoint Object Model grows,

    more detours required for testing • In most cases, more tedious than beneficial • If only could have most of the basic SharePoint behaviors pre-generated • Solution: Microsoft.SharePoint.Behaviors Microsoft.SharePoint.Emulators
  14. Observations • Even behaviors are not complete • A lot

    of mocking activity may get repeated • Unit test setup logic gets refactored into common assemblies • Save time: create scenarios • Use BDD-style approach for testing
  15. Gotchas • More moles unit tests = much longer test

    execution time • TDD is (still) hard • Continuous Integration • This will take time to learn, research, and debug • Pay attention when it stops adding value • The GAC
  16. Another Problem public IList<Machine> GetMachinesWithManufacturer(Manufacturer mft) { using (MachinesSiteDataContext context

    = new MachinesSiteDataContext(SPContext.Current.Web.Url)) { try { var machines = from machine in context.Machines where machine.Active && machine.Manufacturer == mft select machine; return machines.ToList(); } catch (UnauthorizedAccessException e) { return new List<Machine>(); } catch (Exception e) { throw; } } }
  17. Abstraction public interface IMachineRepository { IList<Machine> GetMachinesWithManufacturer(Manufacturer mft); } public

    class MachineRepository : IMachineRepository { public IList<Machine> GetMachinesWithManufacturer(Manufacturer mft) { // ... } }
  18. Abstraction public class MachineWebPart { private IMachineRepository machineRepo; public MachineWebPart()

    { IServiceLocator locator = SharePointServiceLocator.GetCurrent(); this.machineRepo = locator.GetInstance<IMachineRepository>(); } protected IList<Machine> GetMachinesToRender(Manufacturer mft) { return this.machineRepo.GetMachinesWithManufacturer(mft); } }
  19. Q&A