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 depuisTask<T>
s'étendTask
. Vous pouvez utiliserTask.Delay(0)
à la place. Si vous n'avez pas de soins sur la tâche elle-même vous pouvez changerMethodUnderTest
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 êtrereturn _repository.Commit();
si vous supprimez leasync
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()
Vous devez vous connecter pour publier un commentaire.
Vous aurez besoin de revenir à quelque chose;
async Task
méthodes ne peuvent jamais revenirnull
.La
T
n'a pas d'importance. Vous pouvez déclarer unstatic readonly Task SuccessTask = Task.FromResult<object>(null);
comme une aide constante si vous le souhaitez. J'ai similaire constantes dans mon AsyncEx bibliothèque.Si vous ciblez .NET Framework version 4.6 ou ultérieure, vous pouvez maintenant utiliser
.Returns(Task.CompletedTask)
à la place qui est légèrement plus courte.