TDD test class for ViewModel class












1












$begingroup$


I am currently writing my first TDD application. The project is in Xamarin.Forms and tested in xUnit.



I am wondering if maybe more experienced developers will have any comments or suggestions regarding the code or architecture, before I will continue with next View Models, to avoid corrections.



I am using also Autofac and Moq.



Test class:



public class MainPageViewModelTests
{
List<Phrase> phrases;
private MainPageViewModel _viewModel;
private Mock<IPhraseEditViewModel> _phraseEditViewModelMock;
private Mock<IMainDataProvider> _mainDataProviderMock;
public MainPageViewModelTests()
{
//instances
phrases = new List<Phrase>
{
new Phrase { Category = "newCat1", Definition = "newDef1", Group = "newGr1", Learned = false, Name = "newName1", Priority = "newPrio1", Id = 7 }
};
_phraseEditViewModelMock = new Mock<IPhraseEditViewModel>();
_mainDataProviderMock = new Mock<IMainDataProvider>();

//setup
_mainDataProviderMock.Setup(dp => dp.GetGroups())
.Returns(new List<string>
{
"Group #1",
"Group #2",
"Group #3"
});
_mainDataProviderMock.Setup(dp => dp.PickUpFile())
.ReturnsAsync("goodData.csv");
_mainDataProviderMock.Setup(dp => dp.GetStreamFromCSV("goodData.csv"))
.Returns("Name|Definition|Category|Group|Prioritynname1 |def1|cat1|gr1|prio1nname2 |def2|cat2|gr2|prio2");
_mainDataProviderMock.Setup(dp => dp.GetStreamFromCSV("emptyData.csv"))
.Returns("");

//VM instance
_viewModel = new MainPageViewModel(_mainDataProviderMock.Object, CreatePhraseEditViewModel);
}

private IPhraseEditViewModel CreatePhraseEditViewModel() //method for creating PhraseEditVM
{
var phraseEditViewModelMock = new Mock<IPhraseEditViewModel>();
phraseEditViewModelMock.Setup(vm => vm.LoadPhrase(It.IsAny<int>()))
.Callback<int?>(phraseId =>
{
phraseEditViewModelMock.Setup(vm => vm.Phrase)
.Returns(new Phrase());
});
_phraseEditViewModelMock = phraseEditViewModelMock; //field = var(!!)
return phraseEditViewModelMock.Object;
}

[Fact]
public void LoadGroups_ShouldLoadOnce_True()
{
_viewModel.LoadGroups(); //loads groups twice
_viewModel.LoadGroups();

Assert.Equal(3, _viewModel.Groups.Count); //counts how many groups are loaded
}
[Fact]
public void LoadGroups_ShouldLoad_True()
{
_viewModel.LoadGroups(); //loads collection of groups (from setup)
Assert.Equal(3, _viewModel.Groups.Count); //counts groups
var phrase = _viewModel.Groups[0];
Assert.NotNull(phrase);
Assert.Equal("Group #1", phrase); //compares group name
}
[Fact]
public void AddPhrase_ShouldBeExecuted_True()
{
_viewModel.PhraseEdit = false; //set up PhraseEdit prop
_viewModel.AddPhraseCommand.Execute(null); // executes command
Assert.True(_viewModel.PhraseEdit); //verifies PhraseEdit prop
_phraseEditViewModelMock.Verify(vm => vm.LoadPhrase(null), Times.Once); //counts loaded phrases
}
[Fact]
public void LoadFromFile_ShouldConvertReturnedCorrectFormatString_ReturnsPhraseList()
{
_viewModel.LoadFromFile("goodData.csv"); //loads phrases from the file
Assert.Equal(2, _viewModel.LoadedPhrases.Count); //counts loaded phrases from the file
var phrase = _viewModel.LoadedPhrases[0];
Assert.NotNull(phrase); //checks if phrase is not null, below compares props
Assert.Equal("name1", phrase.Name);
Assert.Equal("def1", phrase.Definition);
Assert.Equal("cat1", phrase.Category);
Assert.Equal("gr1", phrase.Group);
Assert.Equal("prio1", phrase.Priority);
}
[Fact]
public void PopulateDb_ShouldSeedDbWithPhrases_CallsDpSavePhrase()
{
_viewModel.LoadedPhrases = phrases; //populates collection
_viewModel.PopulateDb(_viewModel.LoadedPhrases); //populates Db with phase list - 1 item
_mainDataProviderMock.Verify(dp => dp.SavePhrase(It.IsAny<Phrase>()), Times.Once); //counts saved phrases
}
[Fact]
public void LoadFile_ShouldBeExecuted_CallsOnLoadFileExecute()
{
_viewModel.LoadFile.Execute(null); //execute command
Assert.Equal(2, _viewModel.LoadedPhrases.Count()); //counts loaded phrases from the file
Assert.Equal(3, _viewModel.Groups.Count); //counts loaded groups
_mainDataProviderMock.Verify(dp => dp.SavePhrase(It.IsAny<Phrase>()), Times.AtLeast(2)); //counts saved phrases
}
[Fact]
public void PopulateDb_ShouldSeedDbOnce_True()
{
_viewModel.LoadedPhrases = phrases; //populates collection
_viewModel.PopulateDb(_viewModel.LoadedPhrases); //seeds Db twice
_viewModel.PopulateDb(_viewModel.LoadedPhrases);
_mainDataProviderMock.Verify(dp => dp.SavePhrase(It.IsAny<Phrase>()), Times.Once); //should seed only once
}
[Fact]
public void LoadFromFile_WithFilePathParameterIsNull_ReturnsEmptyCollection()
{
List<Phrase> expected = new List<Phrase>();
expected.Clear(); //expectations
List<Phrase> method = _viewModel.LoadFromFile("");//loads phrases from the file with empty path parameter
_viewModel.LoadFromFile(""); //loads phrases from the file with empty path string
Assert.Empty(_viewModel.LoadedPhrases); // check if LoadedPhrases is empty
Assert.Equal(expected, method); //compare expectations with method returns
}
[Fact]
public void PopulateDb_GetsEmptyCollectionParameter_DoesNothing()
{
_viewModel.LoadedPhrases.Clear(); //collection is empty
_viewModel.PopulateDb(_viewModel.LoadedPhrases); //PopulateDb with empty collection
_mainDataProviderMock.Verify(dp => dp.SavePhrase(It.IsAny<Phrase>()), Times.Never); //with empty collection SavePhrase runs never
}
[Fact]
public void LoadFromFile_GetsPathToEmptyFile_ReturnsEmptyCollection()
{
List<Phrase> expected = new List<Phrase>();
expected.Clear(); //expectations
List<Phrase> method = _viewModel.LoadFromFile("emptyData.csv"); //loads phrases from the file with empty content
Assert.Empty(_viewModel.LoadedPhrases); // check if LoadedPhrases is empty
Assert.Equal(expected, method); //compare expectations with method returns
}

//TODO:
//zły format pliku
//brak | w pliku
}


Tested View Model class:



public interface IMainPageViewModel
{
void LoadGroups();
}
public class MainPageViewModel : ViewModelBase, IMainPageViewModel
{
List<Phrase> oldPhrases = new List<Phrase>(); //verification for PopulateDb method;
private Func<IPhraseEditViewModel> _phraseEditVmCreator;
private IMainDataProvider _dataProvider;
public string FileLocation { get; set; }
public ObservableCollection<string> Groups { get; set; }
public List<Phrase> LoadedPhrases { get; set; }
public bool PhraseEdit { get; set; }
public IPhraseEditViewModel SelectedPhraseEditViewModel { get; set; }
public MainPageViewModel(IMainDataProvider dataProvider,
Func<IPhraseEditViewModel> phraseditVmCreator) //ctor
{
_dataProvider = dataProvider;
_phraseEditVmCreator = phraseditVmCreator;
Groups = new ObservableCollection<string>();
LoadedPhrases = new List<Phrase>();
//commands tests
AddPhraseCommand = new DelegateCommand(OnNewPhraseExecute);
LoadFile = new DelegateCommand(OnLoadFileExecute);
}

public ICommand AddPhraseCommand { get; private set; }
public ICommand LoadFile { get; private set; }

private void OnNewPhraseExecute(object obj)
{
SelectedPhraseEditViewModel = CreateAndLoadPhraseEditViewModel(null);
}

private IPhraseEditViewModel CreateAndLoadPhraseEditViewModel(int? phraseId)
{
//Application.Current.MainPage.Navigation.PushAsync(new PhraseEditPage());
var phraseEditVm = _phraseEditVmCreator();
PhraseEdit = true;
phraseEditVm.LoadPhrase(phraseId);
return phraseEditVm;
}
private async void OnLoadFileExecute(object obj)
{
LoadedPhrases.Clear();
FileLocation = await _dataProvider.PickUpFile();
LoadedPhrases = LoadFromFile(FileLocation);
PopulateDb(LoadedPhrases);
LoadGroups();
}
public void LoadGroups() //loads group list from the DB
{
Groups.Clear();
foreach (var group in _dataProvider.GetGroups())
{
Groups.Add(group);
}
}
public List<Phrase> LoadFromFile(string filePath)
{
if (filePath != "")
{
string stream = "";
LoadedPhrases.Clear();
stream = _dataProvider.GetStreamFromCSV(filePath);
Dictionary<string, int> myPhraseMap = new Dictionary<string, int>(); //exception for wrong format
var sr = new StringReader(stream);
using (var csv = new CsvReader(sr, true, '|'))
{
int fieldCount = csv.FieldCount;
string headers = csv.GetFieldHeaders();
for (int i = 0; i < fieldCount; i++)
{
myPhraseMap[headers[i]] = i;
}
while (csv.ReadNextRecord())
{
Phrase phrase = new Phrase
{
Name = csv[myPhraseMap["Name"]],
Definition = csv[myPhraseMap["Definition"]],
Category = csv[myPhraseMap["Category"]],
Group = csv[myPhraseMap["Group"]],
Priority = csv[myPhraseMap["Priority"]],
Learned = false
};
LoadedPhrases.Add(phrase);
}
}
}
else
{
LoadedPhrases.Clear();
}
return LoadedPhrases;
}
public void PopulateDb(List<Phrase> phrases)
{
if (oldPhrases != phrases) //populates only if collection is new
{
foreach (var item in phrases)
{
_dataProvider.SavePhrase(item);
}
oldPhrases = phrases;
}
}
}


GitHub repository of the project










share|improve this question









New contributor




bakunet is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.







$endgroup$

















    1












    $begingroup$


    I am currently writing my first TDD application. The project is in Xamarin.Forms and tested in xUnit.



    I am wondering if maybe more experienced developers will have any comments or suggestions regarding the code or architecture, before I will continue with next View Models, to avoid corrections.



    I am using also Autofac and Moq.



    Test class:



    public class MainPageViewModelTests
    {
    List<Phrase> phrases;
    private MainPageViewModel _viewModel;
    private Mock<IPhraseEditViewModel> _phraseEditViewModelMock;
    private Mock<IMainDataProvider> _mainDataProviderMock;
    public MainPageViewModelTests()
    {
    //instances
    phrases = new List<Phrase>
    {
    new Phrase { Category = "newCat1", Definition = "newDef1", Group = "newGr1", Learned = false, Name = "newName1", Priority = "newPrio1", Id = 7 }
    };
    _phraseEditViewModelMock = new Mock<IPhraseEditViewModel>();
    _mainDataProviderMock = new Mock<IMainDataProvider>();

    //setup
    _mainDataProviderMock.Setup(dp => dp.GetGroups())
    .Returns(new List<string>
    {
    "Group #1",
    "Group #2",
    "Group #3"
    });
    _mainDataProviderMock.Setup(dp => dp.PickUpFile())
    .ReturnsAsync("goodData.csv");
    _mainDataProviderMock.Setup(dp => dp.GetStreamFromCSV("goodData.csv"))
    .Returns("Name|Definition|Category|Group|Prioritynname1 |def1|cat1|gr1|prio1nname2 |def2|cat2|gr2|prio2");
    _mainDataProviderMock.Setup(dp => dp.GetStreamFromCSV("emptyData.csv"))
    .Returns("");

    //VM instance
    _viewModel = new MainPageViewModel(_mainDataProviderMock.Object, CreatePhraseEditViewModel);
    }

    private IPhraseEditViewModel CreatePhraseEditViewModel() //method for creating PhraseEditVM
    {
    var phraseEditViewModelMock = new Mock<IPhraseEditViewModel>();
    phraseEditViewModelMock.Setup(vm => vm.LoadPhrase(It.IsAny<int>()))
    .Callback<int?>(phraseId =>
    {
    phraseEditViewModelMock.Setup(vm => vm.Phrase)
    .Returns(new Phrase());
    });
    _phraseEditViewModelMock = phraseEditViewModelMock; //field = var(!!)
    return phraseEditViewModelMock.Object;
    }

    [Fact]
    public void LoadGroups_ShouldLoadOnce_True()
    {
    _viewModel.LoadGroups(); //loads groups twice
    _viewModel.LoadGroups();

    Assert.Equal(3, _viewModel.Groups.Count); //counts how many groups are loaded
    }
    [Fact]
    public void LoadGroups_ShouldLoad_True()
    {
    _viewModel.LoadGroups(); //loads collection of groups (from setup)
    Assert.Equal(3, _viewModel.Groups.Count); //counts groups
    var phrase = _viewModel.Groups[0];
    Assert.NotNull(phrase);
    Assert.Equal("Group #1", phrase); //compares group name
    }
    [Fact]
    public void AddPhrase_ShouldBeExecuted_True()
    {
    _viewModel.PhraseEdit = false; //set up PhraseEdit prop
    _viewModel.AddPhraseCommand.Execute(null); // executes command
    Assert.True(_viewModel.PhraseEdit); //verifies PhraseEdit prop
    _phraseEditViewModelMock.Verify(vm => vm.LoadPhrase(null), Times.Once); //counts loaded phrases
    }
    [Fact]
    public void LoadFromFile_ShouldConvertReturnedCorrectFormatString_ReturnsPhraseList()
    {
    _viewModel.LoadFromFile("goodData.csv"); //loads phrases from the file
    Assert.Equal(2, _viewModel.LoadedPhrases.Count); //counts loaded phrases from the file
    var phrase = _viewModel.LoadedPhrases[0];
    Assert.NotNull(phrase); //checks if phrase is not null, below compares props
    Assert.Equal("name1", phrase.Name);
    Assert.Equal("def1", phrase.Definition);
    Assert.Equal("cat1", phrase.Category);
    Assert.Equal("gr1", phrase.Group);
    Assert.Equal("prio1", phrase.Priority);
    }
    [Fact]
    public void PopulateDb_ShouldSeedDbWithPhrases_CallsDpSavePhrase()
    {
    _viewModel.LoadedPhrases = phrases; //populates collection
    _viewModel.PopulateDb(_viewModel.LoadedPhrases); //populates Db with phase list - 1 item
    _mainDataProviderMock.Verify(dp => dp.SavePhrase(It.IsAny<Phrase>()), Times.Once); //counts saved phrases
    }
    [Fact]
    public void LoadFile_ShouldBeExecuted_CallsOnLoadFileExecute()
    {
    _viewModel.LoadFile.Execute(null); //execute command
    Assert.Equal(2, _viewModel.LoadedPhrases.Count()); //counts loaded phrases from the file
    Assert.Equal(3, _viewModel.Groups.Count); //counts loaded groups
    _mainDataProviderMock.Verify(dp => dp.SavePhrase(It.IsAny<Phrase>()), Times.AtLeast(2)); //counts saved phrases
    }
    [Fact]
    public void PopulateDb_ShouldSeedDbOnce_True()
    {
    _viewModel.LoadedPhrases = phrases; //populates collection
    _viewModel.PopulateDb(_viewModel.LoadedPhrases); //seeds Db twice
    _viewModel.PopulateDb(_viewModel.LoadedPhrases);
    _mainDataProviderMock.Verify(dp => dp.SavePhrase(It.IsAny<Phrase>()), Times.Once); //should seed only once
    }
    [Fact]
    public void LoadFromFile_WithFilePathParameterIsNull_ReturnsEmptyCollection()
    {
    List<Phrase> expected = new List<Phrase>();
    expected.Clear(); //expectations
    List<Phrase> method = _viewModel.LoadFromFile("");//loads phrases from the file with empty path parameter
    _viewModel.LoadFromFile(""); //loads phrases from the file with empty path string
    Assert.Empty(_viewModel.LoadedPhrases); // check if LoadedPhrases is empty
    Assert.Equal(expected, method); //compare expectations with method returns
    }
    [Fact]
    public void PopulateDb_GetsEmptyCollectionParameter_DoesNothing()
    {
    _viewModel.LoadedPhrases.Clear(); //collection is empty
    _viewModel.PopulateDb(_viewModel.LoadedPhrases); //PopulateDb with empty collection
    _mainDataProviderMock.Verify(dp => dp.SavePhrase(It.IsAny<Phrase>()), Times.Never); //with empty collection SavePhrase runs never
    }
    [Fact]
    public void LoadFromFile_GetsPathToEmptyFile_ReturnsEmptyCollection()
    {
    List<Phrase> expected = new List<Phrase>();
    expected.Clear(); //expectations
    List<Phrase> method = _viewModel.LoadFromFile("emptyData.csv"); //loads phrases from the file with empty content
    Assert.Empty(_viewModel.LoadedPhrases); // check if LoadedPhrases is empty
    Assert.Equal(expected, method); //compare expectations with method returns
    }

    //TODO:
    //zły format pliku
    //brak | w pliku
    }


    Tested View Model class:



    public interface IMainPageViewModel
    {
    void LoadGroups();
    }
    public class MainPageViewModel : ViewModelBase, IMainPageViewModel
    {
    List<Phrase> oldPhrases = new List<Phrase>(); //verification for PopulateDb method;
    private Func<IPhraseEditViewModel> _phraseEditVmCreator;
    private IMainDataProvider _dataProvider;
    public string FileLocation { get; set; }
    public ObservableCollection<string> Groups { get; set; }
    public List<Phrase> LoadedPhrases { get; set; }
    public bool PhraseEdit { get; set; }
    public IPhraseEditViewModel SelectedPhraseEditViewModel { get; set; }
    public MainPageViewModel(IMainDataProvider dataProvider,
    Func<IPhraseEditViewModel> phraseditVmCreator) //ctor
    {
    _dataProvider = dataProvider;
    _phraseEditVmCreator = phraseditVmCreator;
    Groups = new ObservableCollection<string>();
    LoadedPhrases = new List<Phrase>();
    //commands tests
    AddPhraseCommand = new DelegateCommand(OnNewPhraseExecute);
    LoadFile = new DelegateCommand(OnLoadFileExecute);
    }

    public ICommand AddPhraseCommand { get; private set; }
    public ICommand LoadFile { get; private set; }

    private void OnNewPhraseExecute(object obj)
    {
    SelectedPhraseEditViewModel = CreateAndLoadPhraseEditViewModel(null);
    }

    private IPhraseEditViewModel CreateAndLoadPhraseEditViewModel(int? phraseId)
    {
    //Application.Current.MainPage.Navigation.PushAsync(new PhraseEditPage());
    var phraseEditVm = _phraseEditVmCreator();
    PhraseEdit = true;
    phraseEditVm.LoadPhrase(phraseId);
    return phraseEditVm;
    }
    private async void OnLoadFileExecute(object obj)
    {
    LoadedPhrases.Clear();
    FileLocation = await _dataProvider.PickUpFile();
    LoadedPhrases = LoadFromFile(FileLocation);
    PopulateDb(LoadedPhrases);
    LoadGroups();
    }
    public void LoadGroups() //loads group list from the DB
    {
    Groups.Clear();
    foreach (var group in _dataProvider.GetGroups())
    {
    Groups.Add(group);
    }
    }
    public List<Phrase> LoadFromFile(string filePath)
    {
    if (filePath != "")
    {
    string stream = "";
    LoadedPhrases.Clear();
    stream = _dataProvider.GetStreamFromCSV(filePath);
    Dictionary<string, int> myPhraseMap = new Dictionary<string, int>(); //exception for wrong format
    var sr = new StringReader(stream);
    using (var csv = new CsvReader(sr, true, '|'))
    {
    int fieldCount = csv.FieldCount;
    string headers = csv.GetFieldHeaders();
    for (int i = 0; i < fieldCount; i++)
    {
    myPhraseMap[headers[i]] = i;
    }
    while (csv.ReadNextRecord())
    {
    Phrase phrase = new Phrase
    {
    Name = csv[myPhraseMap["Name"]],
    Definition = csv[myPhraseMap["Definition"]],
    Category = csv[myPhraseMap["Category"]],
    Group = csv[myPhraseMap["Group"]],
    Priority = csv[myPhraseMap["Priority"]],
    Learned = false
    };
    LoadedPhrases.Add(phrase);
    }
    }
    }
    else
    {
    LoadedPhrases.Clear();
    }
    return LoadedPhrases;
    }
    public void PopulateDb(List<Phrase> phrases)
    {
    if (oldPhrases != phrases) //populates only if collection is new
    {
    foreach (var item in phrases)
    {
    _dataProvider.SavePhrase(item);
    }
    oldPhrases = phrases;
    }
    }
    }


    GitHub repository of the project










    share|improve this question









    New contributor




    bakunet is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
    Check out our Code of Conduct.







    $endgroup$















      1












      1








      1





      $begingroup$


      I am currently writing my first TDD application. The project is in Xamarin.Forms and tested in xUnit.



      I am wondering if maybe more experienced developers will have any comments or suggestions regarding the code or architecture, before I will continue with next View Models, to avoid corrections.



      I am using also Autofac and Moq.



      Test class:



      public class MainPageViewModelTests
      {
      List<Phrase> phrases;
      private MainPageViewModel _viewModel;
      private Mock<IPhraseEditViewModel> _phraseEditViewModelMock;
      private Mock<IMainDataProvider> _mainDataProviderMock;
      public MainPageViewModelTests()
      {
      //instances
      phrases = new List<Phrase>
      {
      new Phrase { Category = "newCat1", Definition = "newDef1", Group = "newGr1", Learned = false, Name = "newName1", Priority = "newPrio1", Id = 7 }
      };
      _phraseEditViewModelMock = new Mock<IPhraseEditViewModel>();
      _mainDataProviderMock = new Mock<IMainDataProvider>();

      //setup
      _mainDataProviderMock.Setup(dp => dp.GetGroups())
      .Returns(new List<string>
      {
      "Group #1",
      "Group #2",
      "Group #3"
      });
      _mainDataProviderMock.Setup(dp => dp.PickUpFile())
      .ReturnsAsync("goodData.csv");
      _mainDataProviderMock.Setup(dp => dp.GetStreamFromCSV("goodData.csv"))
      .Returns("Name|Definition|Category|Group|Prioritynname1 |def1|cat1|gr1|prio1nname2 |def2|cat2|gr2|prio2");
      _mainDataProviderMock.Setup(dp => dp.GetStreamFromCSV("emptyData.csv"))
      .Returns("");

      //VM instance
      _viewModel = new MainPageViewModel(_mainDataProviderMock.Object, CreatePhraseEditViewModel);
      }

      private IPhraseEditViewModel CreatePhraseEditViewModel() //method for creating PhraseEditVM
      {
      var phraseEditViewModelMock = new Mock<IPhraseEditViewModel>();
      phraseEditViewModelMock.Setup(vm => vm.LoadPhrase(It.IsAny<int>()))
      .Callback<int?>(phraseId =>
      {
      phraseEditViewModelMock.Setup(vm => vm.Phrase)
      .Returns(new Phrase());
      });
      _phraseEditViewModelMock = phraseEditViewModelMock; //field = var(!!)
      return phraseEditViewModelMock.Object;
      }

      [Fact]
      public void LoadGroups_ShouldLoadOnce_True()
      {
      _viewModel.LoadGroups(); //loads groups twice
      _viewModel.LoadGroups();

      Assert.Equal(3, _viewModel.Groups.Count); //counts how many groups are loaded
      }
      [Fact]
      public void LoadGroups_ShouldLoad_True()
      {
      _viewModel.LoadGroups(); //loads collection of groups (from setup)
      Assert.Equal(3, _viewModel.Groups.Count); //counts groups
      var phrase = _viewModel.Groups[0];
      Assert.NotNull(phrase);
      Assert.Equal("Group #1", phrase); //compares group name
      }
      [Fact]
      public void AddPhrase_ShouldBeExecuted_True()
      {
      _viewModel.PhraseEdit = false; //set up PhraseEdit prop
      _viewModel.AddPhraseCommand.Execute(null); // executes command
      Assert.True(_viewModel.PhraseEdit); //verifies PhraseEdit prop
      _phraseEditViewModelMock.Verify(vm => vm.LoadPhrase(null), Times.Once); //counts loaded phrases
      }
      [Fact]
      public void LoadFromFile_ShouldConvertReturnedCorrectFormatString_ReturnsPhraseList()
      {
      _viewModel.LoadFromFile("goodData.csv"); //loads phrases from the file
      Assert.Equal(2, _viewModel.LoadedPhrases.Count); //counts loaded phrases from the file
      var phrase = _viewModel.LoadedPhrases[0];
      Assert.NotNull(phrase); //checks if phrase is not null, below compares props
      Assert.Equal("name1", phrase.Name);
      Assert.Equal("def1", phrase.Definition);
      Assert.Equal("cat1", phrase.Category);
      Assert.Equal("gr1", phrase.Group);
      Assert.Equal("prio1", phrase.Priority);
      }
      [Fact]
      public void PopulateDb_ShouldSeedDbWithPhrases_CallsDpSavePhrase()
      {
      _viewModel.LoadedPhrases = phrases; //populates collection
      _viewModel.PopulateDb(_viewModel.LoadedPhrases); //populates Db with phase list - 1 item
      _mainDataProviderMock.Verify(dp => dp.SavePhrase(It.IsAny<Phrase>()), Times.Once); //counts saved phrases
      }
      [Fact]
      public void LoadFile_ShouldBeExecuted_CallsOnLoadFileExecute()
      {
      _viewModel.LoadFile.Execute(null); //execute command
      Assert.Equal(2, _viewModel.LoadedPhrases.Count()); //counts loaded phrases from the file
      Assert.Equal(3, _viewModel.Groups.Count); //counts loaded groups
      _mainDataProviderMock.Verify(dp => dp.SavePhrase(It.IsAny<Phrase>()), Times.AtLeast(2)); //counts saved phrases
      }
      [Fact]
      public void PopulateDb_ShouldSeedDbOnce_True()
      {
      _viewModel.LoadedPhrases = phrases; //populates collection
      _viewModel.PopulateDb(_viewModel.LoadedPhrases); //seeds Db twice
      _viewModel.PopulateDb(_viewModel.LoadedPhrases);
      _mainDataProviderMock.Verify(dp => dp.SavePhrase(It.IsAny<Phrase>()), Times.Once); //should seed only once
      }
      [Fact]
      public void LoadFromFile_WithFilePathParameterIsNull_ReturnsEmptyCollection()
      {
      List<Phrase> expected = new List<Phrase>();
      expected.Clear(); //expectations
      List<Phrase> method = _viewModel.LoadFromFile("");//loads phrases from the file with empty path parameter
      _viewModel.LoadFromFile(""); //loads phrases from the file with empty path string
      Assert.Empty(_viewModel.LoadedPhrases); // check if LoadedPhrases is empty
      Assert.Equal(expected, method); //compare expectations with method returns
      }
      [Fact]
      public void PopulateDb_GetsEmptyCollectionParameter_DoesNothing()
      {
      _viewModel.LoadedPhrases.Clear(); //collection is empty
      _viewModel.PopulateDb(_viewModel.LoadedPhrases); //PopulateDb with empty collection
      _mainDataProviderMock.Verify(dp => dp.SavePhrase(It.IsAny<Phrase>()), Times.Never); //with empty collection SavePhrase runs never
      }
      [Fact]
      public void LoadFromFile_GetsPathToEmptyFile_ReturnsEmptyCollection()
      {
      List<Phrase> expected = new List<Phrase>();
      expected.Clear(); //expectations
      List<Phrase> method = _viewModel.LoadFromFile("emptyData.csv"); //loads phrases from the file with empty content
      Assert.Empty(_viewModel.LoadedPhrases); // check if LoadedPhrases is empty
      Assert.Equal(expected, method); //compare expectations with method returns
      }

      //TODO:
      //zły format pliku
      //brak | w pliku
      }


      Tested View Model class:



      public interface IMainPageViewModel
      {
      void LoadGroups();
      }
      public class MainPageViewModel : ViewModelBase, IMainPageViewModel
      {
      List<Phrase> oldPhrases = new List<Phrase>(); //verification for PopulateDb method;
      private Func<IPhraseEditViewModel> _phraseEditVmCreator;
      private IMainDataProvider _dataProvider;
      public string FileLocation { get; set; }
      public ObservableCollection<string> Groups { get; set; }
      public List<Phrase> LoadedPhrases { get; set; }
      public bool PhraseEdit { get; set; }
      public IPhraseEditViewModel SelectedPhraseEditViewModel { get; set; }
      public MainPageViewModel(IMainDataProvider dataProvider,
      Func<IPhraseEditViewModel> phraseditVmCreator) //ctor
      {
      _dataProvider = dataProvider;
      _phraseEditVmCreator = phraseditVmCreator;
      Groups = new ObservableCollection<string>();
      LoadedPhrases = new List<Phrase>();
      //commands tests
      AddPhraseCommand = new DelegateCommand(OnNewPhraseExecute);
      LoadFile = new DelegateCommand(OnLoadFileExecute);
      }

      public ICommand AddPhraseCommand { get; private set; }
      public ICommand LoadFile { get; private set; }

      private void OnNewPhraseExecute(object obj)
      {
      SelectedPhraseEditViewModel = CreateAndLoadPhraseEditViewModel(null);
      }

      private IPhraseEditViewModel CreateAndLoadPhraseEditViewModel(int? phraseId)
      {
      //Application.Current.MainPage.Navigation.PushAsync(new PhraseEditPage());
      var phraseEditVm = _phraseEditVmCreator();
      PhraseEdit = true;
      phraseEditVm.LoadPhrase(phraseId);
      return phraseEditVm;
      }
      private async void OnLoadFileExecute(object obj)
      {
      LoadedPhrases.Clear();
      FileLocation = await _dataProvider.PickUpFile();
      LoadedPhrases = LoadFromFile(FileLocation);
      PopulateDb(LoadedPhrases);
      LoadGroups();
      }
      public void LoadGroups() //loads group list from the DB
      {
      Groups.Clear();
      foreach (var group in _dataProvider.GetGroups())
      {
      Groups.Add(group);
      }
      }
      public List<Phrase> LoadFromFile(string filePath)
      {
      if (filePath != "")
      {
      string stream = "";
      LoadedPhrases.Clear();
      stream = _dataProvider.GetStreamFromCSV(filePath);
      Dictionary<string, int> myPhraseMap = new Dictionary<string, int>(); //exception for wrong format
      var sr = new StringReader(stream);
      using (var csv = new CsvReader(sr, true, '|'))
      {
      int fieldCount = csv.FieldCount;
      string headers = csv.GetFieldHeaders();
      for (int i = 0; i < fieldCount; i++)
      {
      myPhraseMap[headers[i]] = i;
      }
      while (csv.ReadNextRecord())
      {
      Phrase phrase = new Phrase
      {
      Name = csv[myPhraseMap["Name"]],
      Definition = csv[myPhraseMap["Definition"]],
      Category = csv[myPhraseMap["Category"]],
      Group = csv[myPhraseMap["Group"]],
      Priority = csv[myPhraseMap["Priority"]],
      Learned = false
      };
      LoadedPhrases.Add(phrase);
      }
      }
      }
      else
      {
      LoadedPhrases.Clear();
      }
      return LoadedPhrases;
      }
      public void PopulateDb(List<Phrase> phrases)
      {
      if (oldPhrases != phrases) //populates only if collection is new
      {
      foreach (var item in phrases)
      {
      _dataProvider.SavePhrase(item);
      }
      oldPhrases = phrases;
      }
      }
      }


      GitHub repository of the project










      share|improve this question









      New contributor




      bakunet is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.







      $endgroup$




      I am currently writing my first TDD application. The project is in Xamarin.Forms and tested in xUnit.



      I am wondering if maybe more experienced developers will have any comments or suggestions regarding the code or architecture, before I will continue with next View Models, to avoid corrections.



      I am using also Autofac and Moq.



      Test class:



      public class MainPageViewModelTests
      {
      List<Phrase> phrases;
      private MainPageViewModel _viewModel;
      private Mock<IPhraseEditViewModel> _phraseEditViewModelMock;
      private Mock<IMainDataProvider> _mainDataProviderMock;
      public MainPageViewModelTests()
      {
      //instances
      phrases = new List<Phrase>
      {
      new Phrase { Category = "newCat1", Definition = "newDef1", Group = "newGr1", Learned = false, Name = "newName1", Priority = "newPrio1", Id = 7 }
      };
      _phraseEditViewModelMock = new Mock<IPhraseEditViewModel>();
      _mainDataProviderMock = new Mock<IMainDataProvider>();

      //setup
      _mainDataProviderMock.Setup(dp => dp.GetGroups())
      .Returns(new List<string>
      {
      "Group #1",
      "Group #2",
      "Group #3"
      });
      _mainDataProviderMock.Setup(dp => dp.PickUpFile())
      .ReturnsAsync("goodData.csv");
      _mainDataProviderMock.Setup(dp => dp.GetStreamFromCSV("goodData.csv"))
      .Returns("Name|Definition|Category|Group|Prioritynname1 |def1|cat1|gr1|prio1nname2 |def2|cat2|gr2|prio2");
      _mainDataProviderMock.Setup(dp => dp.GetStreamFromCSV("emptyData.csv"))
      .Returns("");

      //VM instance
      _viewModel = new MainPageViewModel(_mainDataProviderMock.Object, CreatePhraseEditViewModel);
      }

      private IPhraseEditViewModel CreatePhraseEditViewModel() //method for creating PhraseEditVM
      {
      var phraseEditViewModelMock = new Mock<IPhraseEditViewModel>();
      phraseEditViewModelMock.Setup(vm => vm.LoadPhrase(It.IsAny<int>()))
      .Callback<int?>(phraseId =>
      {
      phraseEditViewModelMock.Setup(vm => vm.Phrase)
      .Returns(new Phrase());
      });
      _phraseEditViewModelMock = phraseEditViewModelMock; //field = var(!!)
      return phraseEditViewModelMock.Object;
      }

      [Fact]
      public void LoadGroups_ShouldLoadOnce_True()
      {
      _viewModel.LoadGroups(); //loads groups twice
      _viewModel.LoadGroups();

      Assert.Equal(3, _viewModel.Groups.Count); //counts how many groups are loaded
      }
      [Fact]
      public void LoadGroups_ShouldLoad_True()
      {
      _viewModel.LoadGroups(); //loads collection of groups (from setup)
      Assert.Equal(3, _viewModel.Groups.Count); //counts groups
      var phrase = _viewModel.Groups[0];
      Assert.NotNull(phrase);
      Assert.Equal("Group #1", phrase); //compares group name
      }
      [Fact]
      public void AddPhrase_ShouldBeExecuted_True()
      {
      _viewModel.PhraseEdit = false; //set up PhraseEdit prop
      _viewModel.AddPhraseCommand.Execute(null); // executes command
      Assert.True(_viewModel.PhraseEdit); //verifies PhraseEdit prop
      _phraseEditViewModelMock.Verify(vm => vm.LoadPhrase(null), Times.Once); //counts loaded phrases
      }
      [Fact]
      public void LoadFromFile_ShouldConvertReturnedCorrectFormatString_ReturnsPhraseList()
      {
      _viewModel.LoadFromFile("goodData.csv"); //loads phrases from the file
      Assert.Equal(2, _viewModel.LoadedPhrases.Count); //counts loaded phrases from the file
      var phrase = _viewModel.LoadedPhrases[0];
      Assert.NotNull(phrase); //checks if phrase is not null, below compares props
      Assert.Equal("name1", phrase.Name);
      Assert.Equal("def1", phrase.Definition);
      Assert.Equal("cat1", phrase.Category);
      Assert.Equal("gr1", phrase.Group);
      Assert.Equal("prio1", phrase.Priority);
      }
      [Fact]
      public void PopulateDb_ShouldSeedDbWithPhrases_CallsDpSavePhrase()
      {
      _viewModel.LoadedPhrases = phrases; //populates collection
      _viewModel.PopulateDb(_viewModel.LoadedPhrases); //populates Db with phase list - 1 item
      _mainDataProviderMock.Verify(dp => dp.SavePhrase(It.IsAny<Phrase>()), Times.Once); //counts saved phrases
      }
      [Fact]
      public void LoadFile_ShouldBeExecuted_CallsOnLoadFileExecute()
      {
      _viewModel.LoadFile.Execute(null); //execute command
      Assert.Equal(2, _viewModel.LoadedPhrases.Count()); //counts loaded phrases from the file
      Assert.Equal(3, _viewModel.Groups.Count); //counts loaded groups
      _mainDataProviderMock.Verify(dp => dp.SavePhrase(It.IsAny<Phrase>()), Times.AtLeast(2)); //counts saved phrases
      }
      [Fact]
      public void PopulateDb_ShouldSeedDbOnce_True()
      {
      _viewModel.LoadedPhrases = phrases; //populates collection
      _viewModel.PopulateDb(_viewModel.LoadedPhrases); //seeds Db twice
      _viewModel.PopulateDb(_viewModel.LoadedPhrases);
      _mainDataProviderMock.Verify(dp => dp.SavePhrase(It.IsAny<Phrase>()), Times.Once); //should seed only once
      }
      [Fact]
      public void LoadFromFile_WithFilePathParameterIsNull_ReturnsEmptyCollection()
      {
      List<Phrase> expected = new List<Phrase>();
      expected.Clear(); //expectations
      List<Phrase> method = _viewModel.LoadFromFile("");//loads phrases from the file with empty path parameter
      _viewModel.LoadFromFile(""); //loads phrases from the file with empty path string
      Assert.Empty(_viewModel.LoadedPhrases); // check if LoadedPhrases is empty
      Assert.Equal(expected, method); //compare expectations with method returns
      }
      [Fact]
      public void PopulateDb_GetsEmptyCollectionParameter_DoesNothing()
      {
      _viewModel.LoadedPhrases.Clear(); //collection is empty
      _viewModel.PopulateDb(_viewModel.LoadedPhrases); //PopulateDb with empty collection
      _mainDataProviderMock.Verify(dp => dp.SavePhrase(It.IsAny<Phrase>()), Times.Never); //with empty collection SavePhrase runs never
      }
      [Fact]
      public void LoadFromFile_GetsPathToEmptyFile_ReturnsEmptyCollection()
      {
      List<Phrase> expected = new List<Phrase>();
      expected.Clear(); //expectations
      List<Phrase> method = _viewModel.LoadFromFile("emptyData.csv"); //loads phrases from the file with empty content
      Assert.Empty(_viewModel.LoadedPhrases); // check if LoadedPhrases is empty
      Assert.Equal(expected, method); //compare expectations with method returns
      }

      //TODO:
      //zły format pliku
      //brak | w pliku
      }


      Tested View Model class:



      public interface IMainPageViewModel
      {
      void LoadGroups();
      }
      public class MainPageViewModel : ViewModelBase, IMainPageViewModel
      {
      List<Phrase> oldPhrases = new List<Phrase>(); //verification for PopulateDb method;
      private Func<IPhraseEditViewModel> _phraseEditVmCreator;
      private IMainDataProvider _dataProvider;
      public string FileLocation { get; set; }
      public ObservableCollection<string> Groups { get; set; }
      public List<Phrase> LoadedPhrases { get; set; }
      public bool PhraseEdit { get; set; }
      public IPhraseEditViewModel SelectedPhraseEditViewModel { get; set; }
      public MainPageViewModel(IMainDataProvider dataProvider,
      Func<IPhraseEditViewModel> phraseditVmCreator) //ctor
      {
      _dataProvider = dataProvider;
      _phraseEditVmCreator = phraseditVmCreator;
      Groups = new ObservableCollection<string>();
      LoadedPhrases = new List<Phrase>();
      //commands tests
      AddPhraseCommand = new DelegateCommand(OnNewPhraseExecute);
      LoadFile = new DelegateCommand(OnLoadFileExecute);
      }

      public ICommand AddPhraseCommand { get; private set; }
      public ICommand LoadFile { get; private set; }

      private void OnNewPhraseExecute(object obj)
      {
      SelectedPhraseEditViewModel = CreateAndLoadPhraseEditViewModel(null);
      }

      private IPhraseEditViewModel CreateAndLoadPhraseEditViewModel(int? phraseId)
      {
      //Application.Current.MainPage.Navigation.PushAsync(new PhraseEditPage());
      var phraseEditVm = _phraseEditVmCreator();
      PhraseEdit = true;
      phraseEditVm.LoadPhrase(phraseId);
      return phraseEditVm;
      }
      private async void OnLoadFileExecute(object obj)
      {
      LoadedPhrases.Clear();
      FileLocation = await _dataProvider.PickUpFile();
      LoadedPhrases = LoadFromFile(FileLocation);
      PopulateDb(LoadedPhrases);
      LoadGroups();
      }
      public void LoadGroups() //loads group list from the DB
      {
      Groups.Clear();
      foreach (var group in _dataProvider.GetGroups())
      {
      Groups.Add(group);
      }
      }
      public List<Phrase> LoadFromFile(string filePath)
      {
      if (filePath != "")
      {
      string stream = "";
      LoadedPhrases.Clear();
      stream = _dataProvider.GetStreamFromCSV(filePath);
      Dictionary<string, int> myPhraseMap = new Dictionary<string, int>(); //exception for wrong format
      var sr = new StringReader(stream);
      using (var csv = new CsvReader(sr, true, '|'))
      {
      int fieldCount = csv.FieldCount;
      string headers = csv.GetFieldHeaders();
      for (int i = 0; i < fieldCount; i++)
      {
      myPhraseMap[headers[i]] = i;
      }
      while (csv.ReadNextRecord())
      {
      Phrase phrase = new Phrase
      {
      Name = csv[myPhraseMap["Name"]],
      Definition = csv[myPhraseMap["Definition"]],
      Category = csv[myPhraseMap["Category"]],
      Group = csv[myPhraseMap["Group"]],
      Priority = csv[myPhraseMap["Priority"]],
      Learned = false
      };
      LoadedPhrases.Add(phrase);
      }
      }
      }
      else
      {
      LoadedPhrases.Clear();
      }
      return LoadedPhrases;
      }
      public void PopulateDb(List<Phrase> phrases)
      {
      if (oldPhrases != phrases) //populates only if collection is new
      {
      foreach (var item in phrases)
      {
      _dataProvider.SavePhrase(item);
      }
      oldPhrases = phrases;
      }
      }
      }


      GitHub repository of the project







      c# xamarin moq






      share|improve this question









      New contributor




      bakunet is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.











      share|improve this question









      New contributor




      bakunet is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.









      share|improve this question




      share|improve this question








      edited 5 mins ago









      Jamal

      30.4k11121227




      30.4k11121227






      New contributor




      bakunet is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.









      asked 5 hours ago









      bakunetbakunet

      61




      61




      New contributor




      bakunet is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.





      New contributor





      bakunet is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.






      bakunet is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.






















          0






          active

          oldest

          votes











          Your Answer





          StackExchange.ifUsing("editor", function () {
          return StackExchange.using("mathjaxEditing", function () {
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          });
          });
          }, "mathjax-editing");

          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "196"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });






          bakunet is a new contributor. Be nice, and check out our Code of Conduct.










          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f216079%2ftdd-test-class-for-viewmodel-class%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          0






          active

          oldest

          votes








          0






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes








          bakunet is a new contributor. Be nice, and check out our Code of Conduct.










          draft saved

          draft discarded


















          bakunet is a new contributor. Be nice, and check out our Code of Conduct.













          bakunet is a new contributor. Be nice, and check out our Code of Conduct.












          bakunet is a new contributor. Be nice, and check out our Code of Conduct.
















          Thanks for contributing an answer to Code Review Stack Exchange!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          Use MathJax to format equations. MathJax reference.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f216079%2ftdd-test-class-for-viewmodel-class%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          Costa Masnaga

          Fotorealismo

          Sidney Franklin