TDD test class for ViewModel class
$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
c# xamarin moq
New contributor
$endgroup$
add a comment |
$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
c# xamarin moq
New contributor
$endgroup$
add a comment |
$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
c# xamarin moq
New contributor
$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
c# xamarin moq
New contributor
New contributor
edited 2 hours ago
Jamal♦
30.4k11121227
30.4k11121227
New contributor
asked 7 hours ago
bakunetbakunet
111
111
New contributor
New contributor
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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.
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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