Developing application for Windows Phone 7 in TDD

  • 1,353 views
Uploaded on

A real example of how to develop an application for Windows Phone 7 with Test Driven Development approach. In this presentation you'll see also hoew to implements the Model-View-ViewModel (MVVM) …

A real example of how to develop an application for Windows Phone 7 with Test Driven Development approach. In this presentation you'll see also hoew to implements the Model-View-ViewModel (MVVM) pattern.

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

Views

Total Views
1,353
On Slideshare
0
From Embeds
0
Number of Embeds
3

Actions

Shares
Downloads
7
Comments
0
Likes
1

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
  • Applicazione base: Dato un utente di soundcloud scaricare la lista delle sue canzoni pubblicate Mostrarla nella schermata principale Cliccando sulla canzone mostrare I suoi dettagli, come l’immagine e la data di pubblicazione E’ solo un esempio giusto per avere qualcosa su cui discutere
  • Ecco le due schermate: La prima con liste delle canzoni box e tasto per cercare La seconda dove mostro I dettagli della canzone Non mi sono messo molto sulla grafica perchè ci server solo per lavorare
  • Proviamo a fare l’applicazione in modo tradizionale
  • Il tipo di dato che andrò a scaricare da SoundCloud è formato così Ho preso un subset di tutte le informazioni che SoundCloud espone per ogni brano
  • View semplicficata Ho tolto alcuni tag xaml per questioni di spazio sulla slide
  • View semplicficata Ho tolto alcuni tag xaml per questioni di spazio sulla slide
  • Parte in alto Una textbox per inserire il nome dell’artista da cercare Un tasto search per effettuare la ricerca e scaricare I dati
  • Un itemcontrol che mostra I brani scaricati
  • Vediamo che cosa viene fatto nel code behind
  • Utilizzo RestSharp che è un componente per poter effettuare la manipolazione di risorse esposte tramite REST. Definisco l’url su cui lavorare Definisco la rappresentazione della risorsa la voglio in json
  • Effettuo la chiamata vera e propria il risultato lo carico come itemsource delll’itemcontrol
  • Definisco il metodo che verrà chiamato quando seleziono una canzone Utilizzo il NavigationService di silverlight per spostarmi nella prossima pagina Passo come parametro alla pagina l’id della canzone che ho selezionato
  • In questa vista mostro il dettaglio della canzone che ho selezionato
  • Sostanzialmente mostro l’immagine collegata alla canzone data di creazione Titolo descrizione
  • Mi aggancio all’evento di navigazione in ingresso alla pagina Reperisco l’id E imposto la chiamata rest
  • Deserializzo le informazioni reperite E le carico nei controlli
  • Problemi con questo approccio Codice fortemente accoppiato con l’interfaccia Lo xaml e il code behind di fatto diventano una classe sola (partial) Quindi il codice non è testabile
  • Per ultima cosa - Mostrare come viene aganciato il viewmodel alla vista
  • Proviamo a farlo in tdd Questa volta avremo due classi separate: Il viewmodel dovee mettiamo tutta la logica dell’applicazione Lo xaml solo per la presentazione Niente più code behind
  • In se non è molto difficile Pensare alla funzionalità su cui si sta lavorando Scrivere in modo dichiarativo il test Verificare che sia rotto Scrivere il codice per farlo passare Verificare che il test sia passato Fare refactor del codice scritto Per il refactor vi consiglio il libro di Fowler
  • Non voglio dilungarmi sul tdd e sul perchè farlo Giusto due cose Tdd serve per ottenere un miglior design del codice I test sono un ottimo effetto collaterale del tdd Disciplina e pratica
  • Nunit come framework di unit test Moq 3.1 per silverlight funziona su wp7
  • Partiamo con la prima storia Voglio scaricare le canzoni di un utente che è stato cercato da Soundcloud
  • immagino che ci sia una lista,intesa come struttura dati che devo mostrare Difficile partire con I test Quindi stupidamente quasi giusto per partire penso che questa lista dovrà essere inizializzata
  • - Faccio questo perchè ho visto spesso che partire è una delle cose piu difficili quando ci si trova di fronte a questo
  • Faccio questo perchè ho visto spesso che partire è una delle cose piu difficili quando ci si trova di fronte a questo Istanzio il viewmodel E verifico che il costruttore mi inizializzi la proprietà track
  • Giusto per essere chiari Serve disciplina Quindi bisogna scriverei minimo codice per fare fallire il test
  • Creo la proprietà
  • E finalemente abbiamo la barra rossa
  • Mi raccomando disciplina Bisgona scrivere il minimo codice per poter fare passare il test
  • Inizializzo la proprietà
  • Aggiungo un backfield all proprietà E rendo la proprietà in sola lettura
  • Faccio riandare I test
  • Ritorniamo alla storia da implementare Un altro test che mi viene in mente è Se schiaccio search il viewmodel deve recuperare la lista delle canzoni
  • Penso che ci sia un servicio che si occuperà di fare la chiamata Rest con RestSharp emi restituirà I dati
  • Per poter disaccoppiare l’implementazione di questo servizio dal viewmodel intriduco un interfaccia Stabilisce il contratto tra le due classi
  • Arriviamo al test e leggiamolo E poi ve lo spiego in dettaglio
  • Come visto prima ho introdotto un servizio che si occupa di reperire per me I dati via rest Per potermi concentrare sul viewmodel in realtà nel test non uso direttamente la mia implementazione del servizio Altrimenti test di integrazione ma non so se il test fallisce di chi è la colpa Per poter fare questo uso dei Mock
  • Simulano il comportamento dell’oggetto che sostituiscono Di fatto implementano l’interfaccia dell’oggetto reale Possono essere più o meno stupidi
  • Altro elemento nuovo e il comando che viene utilizzato dal tasto search - Definito come una proprietà
  • Per fare questo ci appoggiamo all’interfaccia ICommand
  • In realtà l’implementazione che utilizzo io è quella di Prism Dopo vi farò vedere come collegare la proprietà che abbiamo scritto prima con la proprietà command del button
  • Quindi rieccoci al nostro test
  • Per fare compilare dobbiamo aggiungere due proprietà al ViewModel
  • Facciamo girare il test
  • Implementiamo il comando ed effettuiamo la chiamata Proprio per il fatto che devo aggiungere il minimo codice che serve per passare il test nella OnSearch metto solo la chiamata al servizio
  • Rendiamo in sola lettura il comando per il principio dell’incapsulation
  • Altra storia visualizzare la lista Una volta ottenuti I dati dal servizio Vogliamo visualizzarli
  • Istruiamo il mock per rispondere ad una chiamata ben definita Nello specifico a fronte di una chiamata con michelecapra Ritorna una lista con una canzone.
  • Nella assert quindi veirifico che dopo la chiamata ci sia una canzone nella lista delle canzoni
  • In questo caso per poter ottenere il test rosso Non devo fare nulla
  • In questo caso per poter ottenere il test rosso Non devo fare nulla
  • Per passarlo invece devo aggiornare il backfield track con il risultato della chiamata al servizio
  • Per passarlo invece devo aggiornare il backfield track con il risultato della chiamata al servizio
  • Altra storia cliccando sulla canzone voglio vedere il dettaglio
  • Per poter navigare da una pagina all’altra abbiamo bisogno del NavigationService Ma come disaccoppiarlo dal nostro ViewModel?
  • Bellisima implementazione già testata di Laurent Bugnion Per gestire la navigazione tra le pagine
  • In questo caso viene aggiunta una dipendenza nuova del nostro oggetto
  • A differenza del prcedente comando Passo la canzone con un id preciso
  • In questa parte del viewmodel Non cambia nulla
  • Per poter compilare dobbiamo aggiungere un nuovo parametro al costruttore
  • Per poter compilare dobbiamo aggiungere un nuovo parametro al costruttore
  • Per poter passare il test Teniamo il NavigationService da parte in una proprietà privata
  • E implementiamo la chiamata effettiva
  • Passiamo il test
  • niente
  • Rinomino il parametro passato a OnShowDetail da obj in track
  • Rifaccio andare I test
  • Si può fare anche del refactor sulle suite di test
  • inizializzazione del viewmodel e e dei mock dei servizi si ripete
  • Allora ho deciso di creare delle variabili private per la suite di test
  • E di sfruttare un metodo messo a disposizione da Nunit Il Setup viene chiamato prima dell’esecuzione di ogni test
  • Punto di contatto tra il viewmodel e la vista Infatti possiamo sfruttare la proprietà Datacontext per potere metter li il viewmodel
  • In questo modo che è il più grezzo possiblie viene inizializzato il datacontext nella vista quando la si naviga
  • Ssfruttiamo il binding delle proprietà per poter collegare le proprietà del viewmodel con gli oggetti sulla vista
  • Lo stesso discorso viene fatto con la lista delle canzoni
  • Lo stesso discorso viene fatto con la lista delle canzoni
  • Lo stesso discorso viene fatto con la lista delle canzoni
  • Lo stesso discorso viene fatto con la lista delle canzoni

Transcript

  • 1. Sviluppare unapp per WP7 in TDD? Si può fare!
  • 2. Who am I?• Developer freelance: – C# Asp.net Mvc, Wpf, Wp7 – Python Django – Blog: orangecode.it/blog• WEBdeBS founder
  • 3. What we’re going to see• TDD• MVVM• Code..• Code…• …some more code!
  • 4. The show case• Basic application• Download user songs list from SoundCloud.com• Display the list• View song’s detail
  • 5. Screen Shot
  • 6. Let’s do it with code behind
  • 7. Trackpublic class Track{ public int Id{get; set; } public string Kind { get; set; } public string Title { get; set; } public string Description { get; set; } public string Artwork_url { get; set; } public string Created_at { get; set; }}
  • 8. Xaml<phone:PhoneApplicationPage > <Grid x:Name="ContentPanel”> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <StackPanel Orientation="Horizontal"> <TextBox x:Name="searchedText" /> <Button Content="Search" Click="Search" /> </StackPanel>
  • 9. Xaml <ItemsControl Grid.Row="1” x:Name="songsList"> <ItemsControl.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <Image Source="{Binding Image}" /> <TextBlock Text="{Binding Title}"/> </StackPanel> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </Grid></phone:PhoneApplicationPage>
  • 10. Xaml<StackPanel Orientation="Horizontal"> <TextBox x:Name="searchedText" /> <Button Content="Search" Click="Search" /></StackPanel>
  • 11. Xaml<ItemsControl x:Name="songsList"> <ItemsControl.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <Image Source="{Binding Image}”/> <TextBlock Text="{Binding Title}"/> </StackPanel> </DataTemplate> </ItemsControl.ItemTemplate></ItemsControl>
  • 12. Code behind MainViewpublic partial class MainView :PhoneApplicationPage { public MainView() { InitializeComponent(); } private void Search(object sender, EventArgs e) { RestClient client = new RestClient { BaseUrl "http://api.soundcloud.com/users/"+searchedText.Text+"/tracks?client_id=eaf7649b0de68f902a4607c0b730e226" }; var request = new RestRequest { RequestFormat = DataFormat.Json }; client.ExecuteAsync<List<Track>>(request, response => { songsList.ItemsSource = response.Data; }); } private void ShowDetail(object sender, GestureEventArgs e) { NavigationService.Navigate(new Uri("/View/DetailView.xaml?id="+ ((Track)((StackPanel)sender).DataContext).Id, UriKind.Relative)); } }
  • 13. Code behind MainViewprivate void Search(object sender, EventArgs e){ RestClient client = new RestClient { BaseUrl "http://api.soundcloud.com/ users/"+searchedText.Text+"/tracks? client_id=eaf7649b0de68f902a4607c0b730e226" }; var request = new RestRequest { RequestFormat = DataFormat.Json };
  • 14. Code behind MainView client.ExecuteAsync<List<Track>>(request, response => { songsList.ItemsSource = response.Data; });}
  • 15. Code behind MainViewprivate void ShowDetail(object sender, GestureEventArgs e){ NavigationService.Navigate( new Uri( "/View/DetailView.xaml?id="+ ((Track)((StackPanel)sender).DataContext).Id, UriKind.Relative ) );}
  • 16. Xaml DetailView<phone:PhoneApplicationPage x:Class="OrangeCode.SoundCloud.View.MainView” > <Grid x:Name="LayoutRoot" Background="Transparent"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <StackPanel> <Image x:Name="image" Width="200" Height="200” /> <TextBlock x:Name="creationDate" FontSize="22” /> <TextBlock x:Name="title" FontSize="28” /> <TextBlock x:Name="description” TextWrapping=Wrap/> </StackPanel> </Grid> </Grid></phone:PhoneApplicationPage>
  • 17. Xaml DetailView<Grid x:Name="ContentPanel”> <StackPanel> <Image x:Name="image” /> <TextBlock x:Name="creationDate” /> <TextBlock x:Name="title” /> <TextBlock x:Name="description” /> </StackPanel></Grid>
  • 18. Code behind DetailViewpublic partial class DetailView : PhoneApplicationPage { public DetailView() { InitializeComponent(); } protected override void OnNavigatedTo(NavigationEventArgs e) { string id = NavigationContext.QueryString["id"]; RestClient client = new RestClient { BaseUrl = "http://api.soundcloud.com/tracks?client_id=eaf7649b0de68f902a4607c0b730e226&ids=" + id }; var request = new RestRequest { RequestFormat = DataFormat.Json }; client.ExecuteAsync<List<Track>>(request, response => { image.Source = new BitmapImage(new Uri(response.Data[0].Image)); creationDate.Text = response.Data[0].CreationDate; description.Text = response.Data[0].Description; title.Text = response.Data[0].Title; }); } }
  • 19. Code behind DetailViewprotected override void OnNavigatedTo(NavigationEventArgs e){ string id = NavigationContext.QueryString["id"]; RestClient client = new RestClient { BaseUrl = "http://api.soundcloud.com/tracks? client_id=eaf7649b0de68f902a4607c0b730e226&ids=" + id }; var request = new RestRequest { RequestFormat = DataFormat.Json };
  • 20. Code behind DetailView client.ExecuteAsync<List<Track>>(request, response => { image.Source = new BitmapImage( new Uri(response.Data[0].Image) ); creationDate.Text = response.Data[0].CreationDate; description.Text = response.Data[0].Description; title.Text = response.Data[0].Title; });}
  • 21. Problem with code behind• Code coupled with UI – Xaml + Code Behind -> one class• Not testable
  • 22. MVVM approach• Architectural Pattern• Derived from Presentation Model pattern (Fowler)• Clear separation between UI and Logic UI ViewModel Collections, DelegateCommand, Properties
  • 23. MVVM approach• Structure our code: – ViewModel (c#): Logic – View (Xaml): Presentation – No more code behind• Now the ViewModel is testable
  • 24. Test Driven Development• As easy as complex• Life Cycle: – Write test (red) – Write logic to pass the test (green) – Refactor code (refactor) – Again..
  • 25. Test Driven Development• It’s about code design, not test• Test suite are good side effect of tdd• It require a lot of discipline and practice
  • 26. Testing Tools• Nunit for Windows Phone 7• No official mocking framework for Windows Phone 7, but I found out that Moq 3.1 for silverlight works!
  • 27. TDD• Download searched user songs list from SoundCloud.com
  • 28. TDD• There is a list of track that i’ve to show
  • 29. TDDnamespace OrangeCode.SoundCloudFixture{ public class MainViewModelFixture { }}namespace OrangeCode.SoundCloud{ public class MainViewModel { }}
  • 30. TDD -RedWrite test: [Test] public void Constructor_Should_Initialize_TrackList() { MainViewModel viewModel = new MainViewModel(); Assert.IsNotNull(viewModel.Tracks); }
  • 31. TDDYou are not allowed to write anyproduction code unless it is to make afailing unit test pass. Uncle Bob Martin
  • 32. TDD - Redpublic class MainViewModel{ public IList<Track> Tracks { get ; set ; }}
  • 33. TDD
  • 34. TDDYou are not allowed to write any moreproduction code than is sufficient topass the one failing unit test. Uncle Bob Martin
  • 35. TDD - Greenpublic class MainViewModel{ public IList<Track> Tracks { get; set; } public MainViewModel() { Tracks = new List<Track>(); }}
  • 36. TDD
  • 37. TDD –Refactorpublic class MainViewModel{ private IList<Track> _tracks; public IList<Track> Tracks { get { return _tracks; } } public MainViewModel() { _tracks = new List<Track>(); }}
  • 38. TDD - Refactor
  • 39. TDD• Download searched user songs list from SoundCloud.com
  • 40. Architecture MainViewModel Ilist<Track>SearchUserTrack(string ) Search Rest Call Service
  • 41. Architecture public interface ISearchService Main { ViewModel IList<Track> SearchUserTrack(string user); } public class SearchService : ISearchService {ISearchService public IList<Track> SearchUserTrack(string user){ } } Search Rest Call Service
  • 42. TDD[Test]public void Search_Should_RetrieveSearchedUserTrack (){ Mock<ISearchService> service = new Mock<ISearchService>(); MainViewModel viewModel = new MainViewModel(service.Object); viewModel.SearchedText = "michelecapra"; viewModel.Search.Execute(); service.Verify(p=>p.SearchUserTrack("michelecapra"));}
  • 43. TDD[Test]public void Search_Should_RetrieveSearchedUserTrack (){ Mock<ISearchService> service = new Mock<ISearchService>(); MainViewModel viewModel = new MainViewModel(service.Object); viewModel.SearchedText = "michelecapra"; viewModel.Search.Execute(); service.Verify(p=>p.SearchUserTrack("michelecapra"));}
  • 44. TDD - Mock• Simulated objects that mimic the behavior of real objects in controlled ways• Mock objects have the same interface as the real objects they mimic, allowing a client object to remain unaware of whether it is using a real object or a mock object.
  • 45. TDD[Test]public void Search_Should_RetrieveSearchedUserTrack (){ Mock<ISearchService> service = new Mock<ISearchService>(); MainViewModel viewModel = new MainViewModel(service.Object); viewModel.SearchedText = "michelecapra"; viewModel.Search.Execute(); service.Verify(p=>p.SearchUserTrack("michelecapra"));}
  • 46. TDD- ICommand• The ICommand interface enables the abstraction of a parameterized method call through its Execute method.• Typically objects implement this interface to enable method calls on the objects through the use of XAML bindings.
  • 47. TDD- DelegateCommand• ICommand whose delegates can be attached for Execute(T)• Execute(T) is the method to be called when the command is invoked.<Button Command=“”></Button>
  • 48. TDD[Test]public void Search_Should_RetrieveSearchedUserTrack (){ Mock<ISearchService> service = new Mock<ISearchService>(); MainViewModel viewModel = new MainViewModel(service.Object); viewModel.SearchedText = "michelecapra"; viewModel.Search.Execute(); service.Verify(p=>p.SearchUserTrack("michelecapra"));}
  • 49. TDD - RedAdd properties in order to compilepublic string SearchedText { get; set; }public DelegateCommand Search { get; set; }
  • 50. TDD - Red
  • 51. TDD - Greenpublic string SearchedText { get; set; }public DelegateCommand Search { get; set; }public MainViewModel(ISearchService searchService){ _searchService = searchService; _tracks = new List<Track>(); Search = new DelegateCommand(OnSearch);}private void OnSearch(){ _searchService.SearchUserTrack(SearchedText);}
  • 52. TDD - Green
  • 53. TDD - Refactorpublic string SearchedText { get; set; }public DelegateCommand Search { get; private set; }public MainViewModel(ISearchService searchService){ _searchService = searchService; _tracks = new List<Track>(); Search = new DelegateCommand(OnSearch);}private void OnSearch(){ _searchService.SearchUserTrack(SearchedText);}
  • 54. TDD - Refactor
  • 55. TDD• Display the list
  • 56. TDD[Test]public void Search_Should_UpdateTrackList(){ _searchService.Setup(p =>p.SearchUserTrack("michelecapra")).Returns(new List<Track>{newTrack()}); _viewModel.SearchedText = "michelecapra"; _viewModel.Search.Execute(); Assert.AreEqual(_viewModel.Tracks.Count, 1);}
  • 57. TDD[Test]public void Search_Should_UpdateTrackList(){ _searchService.Setup(p =>p.SearchUserTrack("michelecapra")).Returns(new List<Track>{newTrack()}); _viewModel.SearchedText = "michelecapra"; _viewModel.Search.Execute(); Assert.AreEqual(_viewModel.Tracks.Count, 1);}
  • 58. TDD[Test]public void Search_Should_UpdateTrackList(){ _searchService.Setup(p =>p.SearchUserTrack("michelecapra")).Returns(new List<Track>{newTrack()}); _viewModel.SearchedText = "michelecapra"; _viewModel.Search.Execute(); Assert.AreEqual(_viewModel.Tracks.Count, 1);}
  • 59. TDD - Redpublic string SearchedText { get; set; }public DelegateCommand Search { get; private set; }public MainViewModel(ISearchService searchService){ _searchService = searchService; _tracks = new List<Track>(); Search = new DelegateCommand(OnSearch);}private void OnSearch(){ _searchService.SearchUserTrack(SearchedText);}
  • 60. TDD - Red
  • 61. TDD - Greenprivate void OnSearch(){ _tracks= _searchService.SearchUserTrack(SearchedText);}
  • 62. TDD - Green
  • 63. TDD• View song’s detail
  • 64. TDD• We need to introduce the NavigationService• But how to decouple it from our ViewModel?
  • 65. TDD – Navigation Servicepublic interface INavigationService THX to{ void NavigateTo(Uri pageUri); Laurent Bugnion}public class NavigationService : INavigationService{ private static PhoneApplicationFrame _mainFrame; public event NavigatingCancelEventHandler Navigating; public void NavigateTo(Uri pageUri) { … }}
  • 66. TDD [Test] public void ShowDetail_Should_NavigateToDetailView() { var navigationService = new Mock<INavigationService>(); var searchService = new Mock<ISearchService>(); var viewModel = new MainViewModel(searchService.Object,navigationService.Object); viewModel.ShowDetail.Execute(new Track{Id=345}); navigationService.Verify(p => p.NavigateTo(new Uri("/View/DetailView.xaml?id=345", UriKind.Relative)));}
  • 67. TDD [Test] public void ShowDetail_Should_NavigateToDetailView() { var navigationService = new Mock<INavigationService>(); var searchService = new Mock<ISearchService>(); var viewModel = new MainViewModel(searchService.Object,navigationService.Object); viewModel.ShowDetail.Execute(new Track{Id=345}); navigationService.Verify(p => p.NavigateTo(newUri("/View/DetailView.xaml?id=345", UriKind.Relative)));}
  • 68. TDD [Test] public void ShowDetail_Should_NavigateToDetailView() { var navigationService = new Mock<INavigationService>(); var searchService = new Mock<ISearchService>(); var viewModel = new MainViewModel(searchService.Object,navigationService.Object); viewModel.ShowDetail.Execute(new Track{Id=345}); navigationService.Verify(p => p.NavigateTo(newUri("/View/DetailView.xaml?id=345", UriKind.Relative)));}
  • 69. TDD [Test] public void ShowDetail_Should_NavigateToDetailView() { var navigationService = new Mock<INavigationService>(); var searchService = new Mock<ISearchService>(); var viewModel = new MainViewModel(searchService.Object,navigationService.Object); viewModel.ShowDetail.Execute(new Track{Id=345}); navigationService.Verify(p => p.NavigateTo(newUri("/View/DetailView.xaml?id=345", UriKind.Relative)));}
  • 70. TDD - Redpublic class MainViewModel{ private IList<Track> _tracks; public IList<Track> Tracks { get { return _tracks; } } public string SearchedText { get; set; } public DelegateCommand Search { get; private set; } public DelegateCommand<Track> ShowDetail{get; set; }
  • 71. TDD - Redpublic MainViewModel(ISearchService searchService,INavigationService navigationService) { _searchService = searchService; _navigationService = navigationService; _tracks = new List<Track>(); Search = new DelegateCommand(OnSearch); ShowDetail= new DelegateCommand<Track>(OnShowDetail); }
  • 72. TDD - Redpublic MainViewModel(ISearchService searchService,INavigationService navigationService) { _searchService = searchService; _navigationService = navigationService; _tracks = new List<Track>(); Search = new DelegateCommand(OnSearch); ShowDetail= new DelegateCommand<Track>(OnShowDetail); }
  • 73. TDD - Red
  • 74. TDD - Greenprivate readonly INavigationService _navigationService;…public DelegateCommand<Track> ShowDetail{get; set; }… public MainViewModel(ISearchService searchService,INavigationService navigationService) { _searchService = searchService; _navigationService = navigationService; _tracks = new List<Track>(); Search = new DelegateCommand(OnSearch); ShowDetail= new DelegateCommand<Track>(OnShowDetail); }
  • 75. TDD - Greenprivate void OnSearch(){ _tracks= _searchService.SearchUserTrack(SearchedText);}private void OnShowDetail(Track obj){ _navigationService.NavigateTo(new Uri("/View/DetailView.xaml?id="+obj.Id, UriKind.Relative));}
  • 76. TDD - Green
  • 77. TDD - Refactorprivate readonly INavigationService _navigationService;public DelegateCommand<Track> ShowDetail{get; private set; }public MainViewModel(ISearchService searchService,INavigationService navigationService) { _searchService = searchService; _navigationService = navigationService; _tracks = new List<Track>(); Search = new DelegateCommand(OnSearch); ShowDetail= new DelegateCommand<Track>(OnShowDetail); }
  • 78. TDD - Refactorprivate void OnSearch(){ _tracks= _searchService.SearchUserTrack(SearchedText);}private void OnShowDetail(Track track){ _navigationService.NavigateTo(new Uri("/View/DetailView.xaml?id=”+track.Id, UriKind.Relative));}
  • 79. TDD - Refactor
  • 80. TDD – Test suite refactor [Test] public void ShowDetail_Should_NavigateToDetailView() { var navigationService = new Mock<INavigationService>(); var searchService = new Mock<ISearchService>(); var viewModel = new MainViewModel(searchService.Object,navigationService.Object); viewModel.ShowDetail.Execute(new Track{Id=345}); navigationService.Verify(p => p.NavigateTo(newUri("/View/DetailView.xaml?id=345", UriKind.Relative)));}
  • 81. TDD – Test suite refactor [Test] public void ShowDetail_Should_NavigateToDetailView() { var navigationService = new Mock<INavigationService>(); var searchService = new Mock<ISearchService>(); var viewModel = new MainViewModel(searchService.Object,navigationService.Object); viewModel.ShowDetail.Execute(new Track{Id=345}); navigationService.Verify(p => p.NavigateTo(newUri("/View/DetailView.xaml?id=345", UriKind.Relative)));}
  • 82. TDD – Test suite refactorpublic class MainViewModelFixture{ private Mock<INavigationService> _navigationService; private Mock<ISearchService> _searchService; private MainViewModel _viewModel; [SetUp] public void Setup() { _navigationService = new Mock<INavigationService>(); _searchService = new Mock<ISearchService>(); _viewModel = new MainViewModel(_searchService.Object,_navigationService.Object); }
  • 83. TDD – Test suite refactorpublic class MainViewModelFixture{ private Mock<INavigationService> _navigationService; private Mock<ISearchService> _searchService; private MainViewModel _viewModel; [SetUp] public void Setup() { _navigationService = new Mock<INavigationService>(); _searchService = new Mock<ISearchService>(); _viewModel = new MainViewModel(_searchService.Object,_navigationService.Object); }
  • 84. ViewModel and UI• FrameworkElement.DataContext• “A directly embedded object that serves as data context for any bindings within the parent element”• We’ll put here our ViewModel!<phone:PhoneApplicationPage DataContext=“”/>
  • 85. ViewModel and UIpublic partial class MainView :PhoneApplicationPage{ public MainView() { InitializeComponent(); DataContext = new MainViewModel(new SearchService(),new NavigationService()); }}
  • 86. MainView<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <StackPanel Orientation="Horizontal"> <TextBox Text="{Binding Text,Mode=TwoWay}” /> <Button Content="Search" Command="{BindingSearch}" HorizontalAlignment="Right" > </StackPanel>
  • 87. MainView<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <StackPanel Orientation="Horizontal"> <TextBox Text="{Binding Text,Mode=TwoWay}” /> <Button Content="Search" Command="{BindingSearch}" HorizontalAlignment="Right" > </StackPanel>
  • 88. MainView<ItemsControl Grid.Row="1" ItemsSource="{Binding Tracks}” > <ItemsControl.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal" Tap="ShowDetail"> <Image Source="{Binding Image}" /> <TextBlock Text="{Binding Title}"/> </StackPanel> </DataTemplate> </ItemsControl.ItemTemplate></ItemsControl>
  • 89. MainView<ItemsControl Grid.Row="1" ItemsSource="{Binding Tracks}” > <ItemsControl.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal" Tap="ShowDetail"> <Image Source="{Binding Image}" /> <TextBlock Text="{Binding Title}"/> </StackPanel> </DataTemplate> </ItemsControl.ItemTemplate></ItemsControl>
  • 90. INotifyPropertyChanged• INotifyPropertyChanged interface is used to notify clients, typically binding clients, that a property value has changed.
  • 91. INotifyPropertyChangedpublic class MainViewModel : INotifyPropertyChanged{ public event PropertyChangedEventHandler PropertyChanged; private void NotifyPropertyChanged(String name) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(name)); } }
  • 92. INotifyPropertyChanged private void ExecuteSearch() { _tracks=_service.SearchUserTrack(SearchedUser); NotifyPropertyChanged(”Tracks"); }}
  • 93. RecapWhat we have seen:-TDD (unit test, mock)-MVVM (commanding, binding,INotifyPropertyChanged, DataContext)
  • 94. Node.JS
  • 95. Be in contactMail: michele@orangecode.itTwitter: @piccoloaiutanteWeb: www.orangecode.itBlog: www.orangecode.it/blogGitHub: https://github.com/piccoloaiutanteCommunity: WEBdeBS
  • 96. That’s all folks