Stubbing Tâche de retourner méthode async test unitaire

Disons que j'ai la classe suivante et une interface il dépend:

public class MyController
{
    private IRepository _repository;
    public MyController(IRepository repository)
    {
        _repository = repository;
    }

    public async Task MethodUnderTest(int someId)
    {
        var o = await _repository.FindById(someId);
        //update o
        await _repository.Commit();
    }
}

public interface IRepository
{
    Task Commit();
}

Quand je l'unité de tester cette méthode, je peux faire la suite (à l'aide de xUnit et Rhino se moque):

[Fact]
public async Task MyTest()
{
    IRepository repositoryStub = MockRepository.GenerateStub<IRepository>();

    MyController controller = new MyController(repositoryStub);

    await controller.MethodUnderTest(1);
}

Cela échoue avec un Système.NullReferenceException : la référence d'Objet n'est pas définie à une instance d'un objet.

Avec le suivant StackTrace:

UnitTest.MyController.<MethodUnderTest>d__0.MoveNext() in 
\UnitTest\Class1.cs:line 35
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at \UnitTest\Class1.cs:line 20

Est-il exact que cette erreur se produit parce que le Commit() retourne null et la statemachine généré pour l'async/await appels MoveNext() sur un null?

Je peux résoudre ce problème en faisant quelque chose comme:

repositoryStub.Expect(r => r.Commit()).Return(Task.FromResult<bool>(true);

Mais là c'est un peu étrange.. je peux utiliser n'importe T pour FromResult<T> et le test sera exécuté. Je ne peux pas trouver un FromResult méthode qui retourne un non-générique Task.

Importe-t-il ce que j'utilise pour T? Ou devrais-je résoudre ce problème d'une autre façon?

  • Il n'a pas d'importance quel type T vous utilisez depuis Task<T> s'étend Task. Vous pouvez utiliser Task.Delay(0) à la place. Si vous n'avez pas de soins sur la tâche elle-même vous pouvez changer MethodUnderTest de retour _repository.Commit() directement au lieu de créer une méthode asynchrone.
  • Référentiel.Commit() utilise l'Entité Cadres de 6 nouveaux async fonctionnalités pour enregistrer les modifications apportées à la base de données. J'utilise ce à partir d'un ASP.NET application MVC. Qu'entendez-vous par retour de _repository.Commit() directement?
  • Je veux dire MethodUnderTest pourrait être return _repository.Commit(); si vous supprimez le async modificateur, il va juste retourner null tâche dans vos tests si vous n'avez pas écrasé _repository.Commit().
  • OK.. cet exemple est un peu petite. J'ai plusieurs attend à l'intérieur de la MethodUnderTest donc je ne peux pas supprimer le mot-clé async juste pour le Commit()