Testing async method call from constructor
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
1
down vote
favorite
I have a project where I want to build a more sophisticated ToDo list - basically a personal project management system. I'm just starting out with the project, and I'd like some feedback on whether my test methods are OK, as I'm pretty new to TDD. So far, for the main page that lists the daily ToDo items, I've got the following ViewModel:
public class MainVM : ViewModelBase
private IEnumerable<ToDoItem> _toDoItems;
private IRepository<ToDoItem> _toDoItemRepo;
private bool _dataIsLoaded;
public bool DataIsLoaded
get return _dataIsLoaded;
set Set(ref _dataIsLoaded, value, true);
public IEnumerable<ToDoItem> ToDoItems
get return _toDoItems;
set Set(ref _toDoItems, value);
public MainVM(IRepository<ToDoItem> toDoItemRepo)
_toDoItemRepo= toDoItemRepo;
LoadData().ContinueWith(t => FinishedLoadingData(t));
private void FinishedLoadingData(Task loadTask)
switch (loadTask.Status)
case TaskStatus.RanToCompletion:
DataIsLoaded = true;
break;
default:
DataIsLoaded = false;
break;
public async Task LoadData()
if (!DataIsLoaded)
ToDoItems= await _toDoItemRepo.GetAsync();
As you can see, I want to load my data when the ViewModel is created.
I'm using the Set
method from the MVVMLight toolkit to set the DataIsLoaded property- it sets the field behind and raises the PropertyChanged event for me.
I have the following test set up to test for a successful load:
public void DataIsLoaded_is_true_if_loading_task_ran_to_completion()
AutoResetEvent testTrigger = new AutoResetEvent(false);
TaskCompletionSource<IEnumerable<ToDoItem>> taskCompletion = new TaskCompletionSource<IEnumerable<ToDoItem>>();
taskCompletion.SetResult(null);
Mock<IRepository<ToDoItem>> repoMock = new Mock<IRepository<ToDoItem>>();
repoMock.Setup(s => s.GetAsync()).Returns(taskCompletion.Task);
MainVM vm = new MainVM(repoMock.Object);
vm.PropertyChanged += (s, e) =>
if (e.PropertyName == nameof(MainVM.DataIsLoaded))
testTrigger.Set();
;
testTrigger.WaitOne(5000);
Assert.IsTrue(vm.DataIsLoaded);
Now, I can see that there is a potential race condition here. The LoadData
method could complete before the event handler is attached. As an alternative, I could use the Set
method that broadcasts a message to MVVMLight's message handler in my ViewModel, and change my test method to this:
[Test]
public void DataIsLoaded_is_true_if_loading_task_ran_to_completion()
AutoResetEvent testTrigger = new AutoResetEvent(false);
TaskCompletionSource<IEnumerable<ToDoItem>> taskCompletion = new TaskCompletionSource<IEnumerable<ToDoItem>>();
taskCompletion.SetResult(null);
Mock<IRepository<ToDoItem>> repoMock = new Mock<IRepository<ToDoItem>>();
repoMock.Setup(s => s.GetAsync()).Returns(taskCompletion.Task);
Messenger.Default.Register<PropertyChangedMessage<bool>>(
this,
message =>
if (message.PropertyName == nameof(MainVM.DataIsLoaded))
testTrigger.Set();
);
MainVM vm = new MainVM(repoMock.Object);
testTrigger.WaitOne(5000);
Assert.IsTrue(vm.DataIsLoaded);
Now I don't have a race condition, but I'm a bit annoyed that I'm broadcasting a message that I don't really need for my code to work. I could wrap the whole thing in an pre-processor directive, of course, but that's just clunky.
Is there a better way to go about the whole thing?
c# unit-testing async-await mvvm constructor
add a comment |Â
up vote
1
down vote
favorite
I have a project where I want to build a more sophisticated ToDo list - basically a personal project management system. I'm just starting out with the project, and I'd like some feedback on whether my test methods are OK, as I'm pretty new to TDD. So far, for the main page that lists the daily ToDo items, I've got the following ViewModel:
public class MainVM : ViewModelBase
private IEnumerable<ToDoItem> _toDoItems;
private IRepository<ToDoItem> _toDoItemRepo;
private bool _dataIsLoaded;
public bool DataIsLoaded
get return _dataIsLoaded;
set Set(ref _dataIsLoaded, value, true);
public IEnumerable<ToDoItem> ToDoItems
get return _toDoItems;
set Set(ref _toDoItems, value);
public MainVM(IRepository<ToDoItem> toDoItemRepo)
_toDoItemRepo= toDoItemRepo;
LoadData().ContinueWith(t => FinishedLoadingData(t));
private void FinishedLoadingData(Task loadTask)
switch (loadTask.Status)
case TaskStatus.RanToCompletion:
DataIsLoaded = true;
break;
default:
DataIsLoaded = false;
break;
public async Task LoadData()
if (!DataIsLoaded)
ToDoItems= await _toDoItemRepo.GetAsync();
As you can see, I want to load my data when the ViewModel is created.
I'm using the Set
method from the MVVMLight toolkit to set the DataIsLoaded property- it sets the field behind and raises the PropertyChanged event for me.
I have the following test set up to test for a successful load:
public void DataIsLoaded_is_true_if_loading_task_ran_to_completion()
AutoResetEvent testTrigger = new AutoResetEvent(false);
TaskCompletionSource<IEnumerable<ToDoItem>> taskCompletion = new TaskCompletionSource<IEnumerable<ToDoItem>>();
taskCompletion.SetResult(null);
Mock<IRepository<ToDoItem>> repoMock = new Mock<IRepository<ToDoItem>>();
repoMock.Setup(s => s.GetAsync()).Returns(taskCompletion.Task);
MainVM vm = new MainVM(repoMock.Object);
vm.PropertyChanged += (s, e) =>
if (e.PropertyName == nameof(MainVM.DataIsLoaded))
testTrigger.Set();
;
testTrigger.WaitOne(5000);
Assert.IsTrue(vm.DataIsLoaded);
Now, I can see that there is a potential race condition here. The LoadData
method could complete before the event handler is attached. As an alternative, I could use the Set
method that broadcasts a message to MVVMLight's message handler in my ViewModel, and change my test method to this:
[Test]
public void DataIsLoaded_is_true_if_loading_task_ran_to_completion()
AutoResetEvent testTrigger = new AutoResetEvent(false);
TaskCompletionSource<IEnumerable<ToDoItem>> taskCompletion = new TaskCompletionSource<IEnumerable<ToDoItem>>();
taskCompletion.SetResult(null);
Mock<IRepository<ToDoItem>> repoMock = new Mock<IRepository<ToDoItem>>();
repoMock.Setup(s => s.GetAsync()).Returns(taskCompletion.Task);
Messenger.Default.Register<PropertyChangedMessage<bool>>(
this,
message =>
if (message.PropertyName == nameof(MainVM.DataIsLoaded))
testTrigger.Set();
);
MainVM vm = new MainVM(repoMock.Object);
testTrigger.WaitOne(5000);
Assert.IsTrue(vm.DataIsLoaded);
Now I don't have a race condition, but I'm a bit annoyed that I'm broadcasting a message that I don't really need for my code to work. I could wrap the whole thing in an pre-processor directive, of course, but that's just clunky.
Is there a better way to go about the whole thing?
c# unit-testing async-await mvvm constructor
The code I posted is actual code, however. I wasn't sure what I wanted to call my Model, so initially I just called it Thingy, not realising that that would be considered pseudocode. I have edited my question with a better name for my Model class.
â Frauke Nonnenmacher
Mar 1 at 22:44
add a comment |Â
up vote
1
down vote
favorite
up vote
1
down vote
favorite
I have a project where I want to build a more sophisticated ToDo list - basically a personal project management system. I'm just starting out with the project, and I'd like some feedback on whether my test methods are OK, as I'm pretty new to TDD. So far, for the main page that lists the daily ToDo items, I've got the following ViewModel:
public class MainVM : ViewModelBase
private IEnumerable<ToDoItem> _toDoItems;
private IRepository<ToDoItem> _toDoItemRepo;
private bool _dataIsLoaded;
public bool DataIsLoaded
get return _dataIsLoaded;
set Set(ref _dataIsLoaded, value, true);
public IEnumerable<ToDoItem> ToDoItems
get return _toDoItems;
set Set(ref _toDoItems, value);
public MainVM(IRepository<ToDoItem> toDoItemRepo)
_toDoItemRepo= toDoItemRepo;
LoadData().ContinueWith(t => FinishedLoadingData(t));
private void FinishedLoadingData(Task loadTask)
switch (loadTask.Status)
case TaskStatus.RanToCompletion:
DataIsLoaded = true;
break;
default:
DataIsLoaded = false;
break;
public async Task LoadData()
if (!DataIsLoaded)
ToDoItems= await _toDoItemRepo.GetAsync();
As you can see, I want to load my data when the ViewModel is created.
I'm using the Set
method from the MVVMLight toolkit to set the DataIsLoaded property- it sets the field behind and raises the PropertyChanged event for me.
I have the following test set up to test for a successful load:
public void DataIsLoaded_is_true_if_loading_task_ran_to_completion()
AutoResetEvent testTrigger = new AutoResetEvent(false);
TaskCompletionSource<IEnumerable<ToDoItem>> taskCompletion = new TaskCompletionSource<IEnumerable<ToDoItem>>();
taskCompletion.SetResult(null);
Mock<IRepository<ToDoItem>> repoMock = new Mock<IRepository<ToDoItem>>();
repoMock.Setup(s => s.GetAsync()).Returns(taskCompletion.Task);
MainVM vm = new MainVM(repoMock.Object);
vm.PropertyChanged += (s, e) =>
if (e.PropertyName == nameof(MainVM.DataIsLoaded))
testTrigger.Set();
;
testTrigger.WaitOne(5000);
Assert.IsTrue(vm.DataIsLoaded);
Now, I can see that there is a potential race condition here. The LoadData
method could complete before the event handler is attached. As an alternative, I could use the Set
method that broadcasts a message to MVVMLight's message handler in my ViewModel, and change my test method to this:
[Test]
public void DataIsLoaded_is_true_if_loading_task_ran_to_completion()
AutoResetEvent testTrigger = new AutoResetEvent(false);
TaskCompletionSource<IEnumerable<ToDoItem>> taskCompletion = new TaskCompletionSource<IEnumerable<ToDoItem>>();
taskCompletion.SetResult(null);
Mock<IRepository<ToDoItem>> repoMock = new Mock<IRepository<ToDoItem>>();
repoMock.Setup(s => s.GetAsync()).Returns(taskCompletion.Task);
Messenger.Default.Register<PropertyChangedMessage<bool>>(
this,
message =>
if (message.PropertyName == nameof(MainVM.DataIsLoaded))
testTrigger.Set();
);
MainVM vm = new MainVM(repoMock.Object);
testTrigger.WaitOne(5000);
Assert.IsTrue(vm.DataIsLoaded);
Now I don't have a race condition, but I'm a bit annoyed that I'm broadcasting a message that I don't really need for my code to work. I could wrap the whole thing in an pre-processor directive, of course, but that's just clunky.
Is there a better way to go about the whole thing?
c# unit-testing async-await mvvm constructor
I have a project where I want to build a more sophisticated ToDo list - basically a personal project management system. I'm just starting out with the project, and I'd like some feedback on whether my test methods are OK, as I'm pretty new to TDD. So far, for the main page that lists the daily ToDo items, I've got the following ViewModel:
public class MainVM : ViewModelBase
private IEnumerable<ToDoItem> _toDoItems;
private IRepository<ToDoItem> _toDoItemRepo;
private bool _dataIsLoaded;
public bool DataIsLoaded
get return _dataIsLoaded;
set Set(ref _dataIsLoaded, value, true);
public IEnumerable<ToDoItem> ToDoItems
get return _toDoItems;
set Set(ref _toDoItems, value);
public MainVM(IRepository<ToDoItem> toDoItemRepo)
_toDoItemRepo= toDoItemRepo;
LoadData().ContinueWith(t => FinishedLoadingData(t));
private void FinishedLoadingData(Task loadTask)
switch (loadTask.Status)
case TaskStatus.RanToCompletion:
DataIsLoaded = true;
break;
default:
DataIsLoaded = false;
break;
public async Task LoadData()
if (!DataIsLoaded)
ToDoItems= await _toDoItemRepo.GetAsync();
As you can see, I want to load my data when the ViewModel is created.
I'm using the Set
method from the MVVMLight toolkit to set the DataIsLoaded property- it sets the field behind and raises the PropertyChanged event for me.
I have the following test set up to test for a successful load:
public void DataIsLoaded_is_true_if_loading_task_ran_to_completion()
AutoResetEvent testTrigger = new AutoResetEvent(false);
TaskCompletionSource<IEnumerable<ToDoItem>> taskCompletion = new TaskCompletionSource<IEnumerable<ToDoItem>>();
taskCompletion.SetResult(null);
Mock<IRepository<ToDoItem>> repoMock = new Mock<IRepository<ToDoItem>>();
repoMock.Setup(s => s.GetAsync()).Returns(taskCompletion.Task);
MainVM vm = new MainVM(repoMock.Object);
vm.PropertyChanged += (s, e) =>
if (e.PropertyName == nameof(MainVM.DataIsLoaded))
testTrigger.Set();
;
testTrigger.WaitOne(5000);
Assert.IsTrue(vm.DataIsLoaded);
Now, I can see that there is a potential race condition here. The LoadData
method could complete before the event handler is attached. As an alternative, I could use the Set
method that broadcasts a message to MVVMLight's message handler in my ViewModel, and change my test method to this:
[Test]
public void DataIsLoaded_is_true_if_loading_task_ran_to_completion()
AutoResetEvent testTrigger = new AutoResetEvent(false);
TaskCompletionSource<IEnumerable<ToDoItem>> taskCompletion = new TaskCompletionSource<IEnumerable<ToDoItem>>();
taskCompletion.SetResult(null);
Mock<IRepository<ToDoItem>> repoMock = new Mock<IRepository<ToDoItem>>();
repoMock.Setup(s => s.GetAsync()).Returns(taskCompletion.Task);
Messenger.Default.Register<PropertyChangedMessage<bool>>(
this,
message =>
if (message.PropertyName == nameof(MainVM.DataIsLoaded))
testTrigger.Set();
);
MainVM vm = new MainVM(repoMock.Object);
testTrigger.WaitOne(5000);
Assert.IsTrue(vm.DataIsLoaded);
Now I don't have a race condition, but I'm a bit annoyed that I'm broadcasting a message that I don't really need for my code to work. I could wrap the whole thing in an pre-processor directive, of course, but that's just clunky.
Is there a better way to go about the whole thing?
c# unit-testing async-await mvvm constructor
edited Mar 2 at 0:01
200_success
123k14142399
123k14142399
asked Mar 1 at 18:53
Frauke Nonnenmacher
1146
1146
The code I posted is actual code, however. I wasn't sure what I wanted to call my Model, so initially I just called it Thingy, not realising that that would be considered pseudocode. I have edited my question with a better name for my Model class.
â Frauke Nonnenmacher
Mar 1 at 22:44
add a comment |Â
The code I posted is actual code, however. I wasn't sure what I wanted to call my Model, so initially I just called it Thingy, not realising that that would be considered pseudocode. I have edited my question with a better name for my Model class.
â Frauke Nonnenmacher
Mar 1 at 22:44
The code I posted is actual code, however. I wasn't sure what I wanted to call my Model, so initially I just called it Thingy, not realising that that would be considered pseudocode. I have edited my question with a better name for my Model class.
â Frauke Nonnenmacher
Mar 1 at 22:44
The code I posted is actual code, however. I wasn't sure what I wanted to call my Model, so initially I just called it Thingy, not realising that that would be considered pseudocode. I have edited my question with a better name for my Model class.
â Frauke Nonnenmacher
Mar 1 at 22:44
add a comment |Â
1 Answer
1
active
oldest
votes
up vote
3
down vote
accepted
Trying to load the data asynchronously in the constructor is a bad idea and in my opinion goes against what the constructor is for.
Constructors cannot be async, and asynchronous initialization can be seen as an implementation detail.
I suggest separating the initialization and data loading out ...
public class MainVM : ViewModelBase
private readonly IRepository<ToDoItem> repository;
private IEnumerable<ToDoItem> items;
private bool _dataIsLoaded;
private bool loading = false;
public bool DataIsLoaded
get return _dataIsLoaded;
set Set(ref _dataIsLoaded, value, true);
public IEnumerable<ToDoItem> ToDoItems
get return items;
set Set(ref items, value);
public MainVM(IRepository<ToDoItem> repository)
this.repository = repository;
public async Task LoadData()
if (loading) return;
if (!DataIsLoaded)
try
loading = true;
ToDoItems = await repository.GetAsync();
loading = false;
DataIsLoaded = true; // task Ran To Completion
catch (Exception ex)
loading = false;
//TODO: Log error
DataIsLoaded = false; // task did not complete
... and have whatever is binding/using to the view model (ie the view, another model, a test, etc) load the data after initialization.
This allows the testing to be much simpler and to the point
For example
[TestClass]
public class MainVmTests
[TestMethod]
public async Task DataIsLoaded_is_true_if_loading_task_ran_to_completion()
//Arrange
var repoMock = new Mock<IRepository<ToDoItem>>();
repoMock.Setup(_ => _.GetAsync()).Returns(Task.FromResult(Enumerable.Empty<ToDoItem>()));
var subject = new MainVM(repoMock.Object);
//Act
await subject.LoadData();
//Assert
Assert.IsTrue(subject.DataIsLoaded);
Assert.IsNotNull(subject.ToDoItems);
[TestMethod]
public async Task DataIsLoaded_is_false_if_loading_task_failed()
//Arrange
var repoMock = new Mock<IRepository<ToDoItem>>();
repoMock.Setup(_ => _.GetAsync()).Throws(new AggregateException());
var subject = new MainVM(repoMock.Object);
//Act
await subject.LoadData();
//Assert
Assert.IsFalse(subject.DataIsLoaded);
Assert.IsNull(subject.ToDoItems);
add a comment |Â
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
3
down vote
accepted
Trying to load the data asynchronously in the constructor is a bad idea and in my opinion goes against what the constructor is for.
Constructors cannot be async, and asynchronous initialization can be seen as an implementation detail.
I suggest separating the initialization and data loading out ...
public class MainVM : ViewModelBase
private readonly IRepository<ToDoItem> repository;
private IEnumerable<ToDoItem> items;
private bool _dataIsLoaded;
private bool loading = false;
public bool DataIsLoaded
get return _dataIsLoaded;
set Set(ref _dataIsLoaded, value, true);
public IEnumerable<ToDoItem> ToDoItems
get return items;
set Set(ref items, value);
public MainVM(IRepository<ToDoItem> repository)
this.repository = repository;
public async Task LoadData()
if (loading) return;
if (!DataIsLoaded)
try
loading = true;
ToDoItems = await repository.GetAsync();
loading = false;
DataIsLoaded = true; // task Ran To Completion
catch (Exception ex)
loading = false;
//TODO: Log error
DataIsLoaded = false; // task did not complete
... and have whatever is binding/using to the view model (ie the view, another model, a test, etc) load the data after initialization.
This allows the testing to be much simpler and to the point
For example
[TestClass]
public class MainVmTests
[TestMethod]
public async Task DataIsLoaded_is_true_if_loading_task_ran_to_completion()
//Arrange
var repoMock = new Mock<IRepository<ToDoItem>>();
repoMock.Setup(_ => _.GetAsync()).Returns(Task.FromResult(Enumerable.Empty<ToDoItem>()));
var subject = new MainVM(repoMock.Object);
//Act
await subject.LoadData();
//Assert
Assert.IsTrue(subject.DataIsLoaded);
Assert.IsNotNull(subject.ToDoItems);
[TestMethod]
public async Task DataIsLoaded_is_false_if_loading_task_failed()
//Arrange
var repoMock = new Mock<IRepository<ToDoItem>>();
repoMock.Setup(_ => _.GetAsync()).Throws(new AggregateException());
var subject = new MainVM(repoMock.Object);
//Act
await subject.LoadData();
//Assert
Assert.IsFalse(subject.DataIsLoaded);
Assert.IsNull(subject.ToDoItems);
add a comment |Â
up vote
3
down vote
accepted
Trying to load the data asynchronously in the constructor is a bad idea and in my opinion goes against what the constructor is for.
Constructors cannot be async, and asynchronous initialization can be seen as an implementation detail.
I suggest separating the initialization and data loading out ...
public class MainVM : ViewModelBase
private readonly IRepository<ToDoItem> repository;
private IEnumerable<ToDoItem> items;
private bool _dataIsLoaded;
private bool loading = false;
public bool DataIsLoaded
get return _dataIsLoaded;
set Set(ref _dataIsLoaded, value, true);
public IEnumerable<ToDoItem> ToDoItems
get return items;
set Set(ref items, value);
public MainVM(IRepository<ToDoItem> repository)
this.repository = repository;
public async Task LoadData()
if (loading) return;
if (!DataIsLoaded)
try
loading = true;
ToDoItems = await repository.GetAsync();
loading = false;
DataIsLoaded = true; // task Ran To Completion
catch (Exception ex)
loading = false;
//TODO: Log error
DataIsLoaded = false; // task did not complete
... and have whatever is binding/using to the view model (ie the view, another model, a test, etc) load the data after initialization.
This allows the testing to be much simpler and to the point
For example
[TestClass]
public class MainVmTests
[TestMethod]
public async Task DataIsLoaded_is_true_if_loading_task_ran_to_completion()
//Arrange
var repoMock = new Mock<IRepository<ToDoItem>>();
repoMock.Setup(_ => _.GetAsync()).Returns(Task.FromResult(Enumerable.Empty<ToDoItem>()));
var subject = new MainVM(repoMock.Object);
//Act
await subject.LoadData();
//Assert
Assert.IsTrue(subject.DataIsLoaded);
Assert.IsNotNull(subject.ToDoItems);
[TestMethod]
public async Task DataIsLoaded_is_false_if_loading_task_failed()
//Arrange
var repoMock = new Mock<IRepository<ToDoItem>>();
repoMock.Setup(_ => _.GetAsync()).Throws(new AggregateException());
var subject = new MainVM(repoMock.Object);
//Act
await subject.LoadData();
//Assert
Assert.IsFalse(subject.DataIsLoaded);
Assert.IsNull(subject.ToDoItems);
add a comment |Â
up vote
3
down vote
accepted
up vote
3
down vote
accepted
Trying to load the data asynchronously in the constructor is a bad idea and in my opinion goes against what the constructor is for.
Constructors cannot be async, and asynchronous initialization can be seen as an implementation detail.
I suggest separating the initialization and data loading out ...
public class MainVM : ViewModelBase
private readonly IRepository<ToDoItem> repository;
private IEnumerable<ToDoItem> items;
private bool _dataIsLoaded;
private bool loading = false;
public bool DataIsLoaded
get return _dataIsLoaded;
set Set(ref _dataIsLoaded, value, true);
public IEnumerable<ToDoItem> ToDoItems
get return items;
set Set(ref items, value);
public MainVM(IRepository<ToDoItem> repository)
this.repository = repository;
public async Task LoadData()
if (loading) return;
if (!DataIsLoaded)
try
loading = true;
ToDoItems = await repository.GetAsync();
loading = false;
DataIsLoaded = true; // task Ran To Completion
catch (Exception ex)
loading = false;
//TODO: Log error
DataIsLoaded = false; // task did not complete
... and have whatever is binding/using to the view model (ie the view, another model, a test, etc) load the data after initialization.
This allows the testing to be much simpler and to the point
For example
[TestClass]
public class MainVmTests
[TestMethod]
public async Task DataIsLoaded_is_true_if_loading_task_ran_to_completion()
//Arrange
var repoMock = new Mock<IRepository<ToDoItem>>();
repoMock.Setup(_ => _.GetAsync()).Returns(Task.FromResult(Enumerable.Empty<ToDoItem>()));
var subject = new MainVM(repoMock.Object);
//Act
await subject.LoadData();
//Assert
Assert.IsTrue(subject.DataIsLoaded);
Assert.IsNotNull(subject.ToDoItems);
[TestMethod]
public async Task DataIsLoaded_is_false_if_loading_task_failed()
//Arrange
var repoMock = new Mock<IRepository<ToDoItem>>();
repoMock.Setup(_ => _.GetAsync()).Throws(new AggregateException());
var subject = new MainVM(repoMock.Object);
//Act
await subject.LoadData();
//Assert
Assert.IsFalse(subject.DataIsLoaded);
Assert.IsNull(subject.ToDoItems);
Trying to load the data asynchronously in the constructor is a bad idea and in my opinion goes against what the constructor is for.
Constructors cannot be async, and asynchronous initialization can be seen as an implementation detail.
I suggest separating the initialization and data loading out ...
public class MainVM : ViewModelBase
private readonly IRepository<ToDoItem> repository;
private IEnumerable<ToDoItem> items;
private bool _dataIsLoaded;
private bool loading = false;
public bool DataIsLoaded
get return _dataIsLoaded;
set Set(ref _dataIsLoaded, value, true);
public IEnumerable<ToDoItem> ToDoItems
get return items;
set Set(ref items, value);
public MainVM(IRepository<ToDoItem> repository)
this.repository = repository;
public async Task LoadData()
if (loading) return;
if (!DataIsLoaded)
try
loading = true;
ToDoItems = await repository.GetAsync();
loading = false;
DataIsLoaded = true; // task Ran To Completion
catch (Exception ex)
loading = false;
//TODO: Log error
DataIsLoaded = false; // task did not complete
... and have whatever is binding/using to the view model (ie the view, another model, a test, etc) load the data after initialization.
This allows the testing to be much simpler and to the point
For example
[TestClass]
public class MainVmTests
[TestMethod]
public async Task DataIsLoaded_is_true_if_loading_task_ran_to_completion()
//Arrange
var repoMock = new Mock<IRepository<ToDoItem>>();
repoMock.Setup(_ => _.GetAsync()).Returns(Task.FromResult(Enumerable.Empty<ToDoItem>()));
var subject = new MainVM(repoMock.Object);
//Act
await subject.LoadData();
//Assert
Assert.IsTrue(subject.DataIsLoaded);
Assert.IsNotNull(subject.ToDoItems);
[TestMethod]
public async Task DataIsLoaded_is_false_if_loading_task_failed()
//Arrange
var repoMock = new Mock<IRepository<ToDoItem>>();
repoMock.Setup(_ => _.GetAsync()).Throws(new AggregateException());
var subject = new MainVM(repoMock.Object);
//Act
await subject.LoadData();
//Assert
Assert.IsFalse(subject.DataIsLoaded);
Assert.IsNull(subject.ToDoItems);
edited Mar 3 at 12:05
answered Mar 3 at 11:24
Nkosi
1,870619
1,870619
add a comment |Â
add a comment |Â
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
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f188634%2ftesting-async-method-call-from-constructor%23new-answer', 'question_page');
);
Post as a guest
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
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
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
The code I posted is actual code, however. I wasn't sure what I wanted to call my Model, so initially I just called it Thingy, not realising that that would be considered pseudocode. I have edited my question with a better name for my Model class.
â Frauke Nonnenmacher
Mar 1 at 22:44