Testing async method call from constructor

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
1
down vote

favorite
1












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?







share|improve this question





















  • 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
















up vote
1
down vote

favorite
1












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?







share|improve this question





















  • 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












up vote
1
down vote

favorite
1









up vote
1
down vote

favorite
1






1





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?







share|improve this question













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?









share|improve this question












share|improve this question




share|improve this question








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
















  • 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










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);







share|improve this answer























    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',
    convertImagesToLinks: false,
    noModals: false,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    );



    );








     

    draft saved


    draft discarded


















    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






























    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);







    share|improve this answer



























      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);







      share|improve this answer

























        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);







        share|improve this answer















        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);








        share|improve this answer















        share|improve this answer



        share|improve this answer








        edited Mar 3 at 12:05


























        answered Mar 3 at 11:24









        Nkosi

        1,870619




        1,870619






















             

            draft saved


            draft discarded


























             


            draft saved


            draft discarded














            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













































































            Popular posts from this blog

            Greedy Best First Search implementation in Rust

            Function to Return a JSON Like Objects Using VBA Collections and Arrays

            C++11 CLH Lock Implementation