Para simular um método assíncrono com o Moq, configure-o com ReturnsAsync() em vez de Returns(). Para um método que retorna Task<T>, ReturnsAsync(value) retorna uma tarefa já concluída que encapsula esse valor, então o código sob teste pode aguardá-la normalmente.
Para um método que retorna uma Task simples, sem resultado, não há nada a encapsular, então a configuração usa Returns(Task.CompletedTask). Essas duas linhas cobrem quase todos os mocks assíncronos que vamos escrever, e o restante do artigo mostra as duas aplicadas a um repositório real.
Como usar ReturnsAsync no Moq?
ReturnsAsync() é o método de configuração do Moq para qualquer método simulado cujo tipo de retorno seja Task<T>. Ele recebe o valor que queremos entregar a quem chama, e o Moq o encapsula em uma tarefa já concluída.
var mock = new Mock<IArticleRepository>();
mock.Setup(x => x.GetArticleAsync(It.IsAny<int>()))
.ReturnsAsync(new ArticleDbModel { Id = 1, Title = "First Article" });
Agora, aguardar GetArticleAsync(1) no código sob teste retorna esse artigo de forma síncrona e sem troca de thread, porque a tarefa já está concluída antes de a execução chegar ao await.
Usar o Returns() comum aqui é o erro que vale a pena conhecer. Returns(article) não compila, porque a configuração espera uma Task<ArticleDbModel>; já Returns(Task.FromResult(article)) compila. É para essa forma que ReturnsAsync() serve de atalho, e é ela que aparece no código mais antigo com Moq.
Quando o valor precisa depender dos argumentos, ReturnsAsync também aceita uma expressão lambda que os recebe, então ReturnsAsync((int id) => _articles.First(a => a.Id == id)) retorna um artigo diferente a cada chamada.
SetupSequence() trata o caso em que chamadas sucessivas devem retornar valores diferentes: um teste de novas tentativas que falha uma vez e depois é bem-sucedido é o motivo mais comum. Cada ReturnsAsync() encadeado na sequência cobre uma chamada, na ordem.
A mesma ideia, baseada nos argumentos, está por trás de retornar um valor que foi passado para um método simulado.
Como simular um método que retorna uma Task simples?
Quando o método retorna Task sem resultado, não há valor a encapsular, então ReturnsAsync() não se aplica. A configuração retorna diretamente uma tarefa concluída.
mock.Setup(x => x.SaveArticleAsync(It.IsAny<ArticleDbModel>()))
.Returns(Task.CompletedTask);
Um método assíncrono no formato void parece pedir o método de configuração assíncrono, mas ReturnsAsync() não compila com ele, e a solução é o Returns(), de aparência mais antiga.
Omitir a configuração por completo leva a uma falha mais estranha. Um mock flexível (loose) retorna uma tarefa já concluída para um método Task<T> não configurado, então o await é bem-sucedido e retorna default(T), ou uma sequência vazia quando T é um array, IEnumerable ou IQueryable. O nosso próprio código lança uma exceção por causa desse null algumas linhas depois.
Vale a pena reconhecer essa falha logo de cara, porque o stack trace aponta para o nosso próprio código de produção, e não para o teste que esqueceu uma configuração. Já um mock estrito (new Mock<T>(MockBehavior.Strict)) transforma essa falha em um erro explícito de “configuração ausente”.
Um caso relacionado segue a mesma regra: use Returns(Task.FromException(ex)) para colocar a tarefa em estado de falha, ou ThrowsAsync(ex), que é mais legível e faz a mesma coisa.
O projeto de repositório que vamos testar
O projeto MockAsynchronousMethods.Repository representa um padrão Repository que conecta o nosso código-fonte a um banco de dados falso, o mesmo formato de repositório que usaríamos para criar testes unitários de controladores com o Moq.
Como a criação do repositório não é o objetivo principal deste artigo, não vamos mostrar todas as classes e interfaces, apenas a classe principal que vamos testar. Dito isso, vamos examinar a classe ArticleRepository:
public class ArticleRepository : IArticleRepository
{
private readonly IFakeDbArticles _fakeDbArticle;
public ArticleRepository(IFakeDbArticles fakeDbArticle)
{
_fakeDbArticle = fakeDbArticle;
}
public async Task<ArticleDbModel?> GetArticleAsync(int id)
{
return await _fakeDbArticle.GetByIdAsync(id);
}
public async Task<IEnumerable<ArticleDbModel>> GetAllArticlesAsync()
{
return await _fakeDbArticle.GetAsync();
}
}
Essa classe implementa a interface IArticleRepository. Observe que todos esses métodos são assíncronos e simplesmente obtêm dados da classe FakeDbArticles:
public class FakeDbArticles : List<ArticleDbModel>, IFakeDbArticles
{
private readonly static List<ArticleDbModel> _articles = Populate();
private static List<ArticleDbModel> Populate()
{
var result = new List<ArticleDbModel>()
{
new ArticleDbModel
{
Id = 1,
Title = "First Article",
LastUpdate = DateTime.Now
},
...
};
return result;
}
public async Task<IEnumerable<ArticleDbModel>> GetAsync()
{
return await Task.FromResult(_articles);
}
public async Task<ArticleDbModel?> GetByIdAsync(int id)
{
return await Task.FromResult(_articles.FirstOrDefault(x => x.Id == id));
}
}
Para mais detalhes, você pode conferir a implementação completa desse repositório no código-fonte deste artigo.

E-book gratuito
Sua Web API está pronta para produção?
33 itens para verificar antes de implantá-la, com a correção de cada um. Um PDF gratuito de 76 páginas para .NET 10.
O e-book está em inglês.
Baixe o checklist gratuitoPDF gratuito. Um único e-mail para enviá-lo. Cancele a inscrição quando quiser.
Projeto de teste xUnit para simular métodos assíncronos
Agora que o repositório está pronto, vamos criar o projeto de teste xUnit MockAsynchronousMethods.Tests. Veja o artigo sobre testes unitários com xUnit se o próprio framework ainda for novidade.
Com o projeto pronto, vamos adicionar MockAsynchronousMethods.Repository como referência, clicando com o botão direito nas dependências e depois em Add Project Reference.
Na próxima etapa, precisamos instalar o framework Moq:
Install-Package Moq
Feito isso, vamos criar uma pasta Mock e, dentro dela, uma classe FakeDbArticleMock que herda de Mock<IFakeDbArticles>.
using MockAsynchronousMethods.Repository.Interfaces;
using Moq;
public class FakeDbArticleMock : Mock<IFakeDbArticles>
{
}
E uma classe FakeDb para guardar uma lista com alguns artigos:
using MockAsynchronousMethods.Repository.DbModels;
public static class FakeDb
{
public static List<ArticleDbModel> Articles = new List<ArticleDbModel>()
{
new ArticleDbModel
{
Id = 1,
Title = "First Article",
LastUpdate = DateTime.Now
},
new ArticleDbModel
{
Id = 2,
Title = "Second title",
LastUpdate = DateTime.Now
},
new ArticleDbModel
{
Id = 3,
Title = "Third title",
LastUpdate = DateTime.Now
}
};
}
Agora, vamos começar a configurar os nossos mocks:
public FakeDbArticleMock GetByIdAsync()
{
Setup(x => x.GetByIdAsync(It.IsAny<int>()))
.ReturnsAsync(FakeDb.Articles.First());
return this;
}
Nesse método, configuramos uma chamada assíncrona simulada ao método GetByIdAsync da interface IFakeDbArticles. Esse método retorna o primeiro artigo da lista que criamos dentro da classe FakeDb.
Quando simulamos um método assíncrono que retorna um valor, em vez do método Returns(...) comum, usamos o método ReturnsAsync(...), que faz o nosso mock retornar uma tarefa concluída.
Você pode conferir os outros métodos de mock na implementação completa da classe FakeDbArticleMock aqui.
Agora que todos os mocks estão prontos, é hora de testar o nosso código.

E-book gratuito
Sua Web API está pronta para produção?
33 itens para verificar antes de implantá-la, com a correção de cada um. Um PDF gratuito de 76 páginas para .NET 10.
O e-book está em inglês.
Baixe o checklist gratuitoPDF gratuito. Um único e-mail para enviá-lo. Cancele a inscrição quando quiser.
Na raiz do projeto de teste, vamos criar uma classe ArticleRepositoryTests, responsável por reunir todos os métodos de teste deste artigo. O arquivo dela começa com diretivas using para MockAsynchronousMethods.Repository, MockAsynchronousMethods.Tests.Mock e Moq.
Em seguida, vamos criar um método de teste:
[Fact]
public async Task GivenAMockedDatabase_WhenRequestingAnExistingArticleAsynchronously_ThenReturnASingleArticle()
{
var mockArticleRepository = new FakeDbArticleMock()
.GetByIdAsync();
var articleRepository = new ArticleRepository(mockArticleRepository.Object);
var result = await articleRepository.GetArticleAsync(1);
Assert.NotNull(result);
Assert.Equal(FakeDb.Articles.First(), result);
}
Primeiro, instanciamos a classe FakeDbArticleMock e indicamos qual configuração queremos usar neste teste. Depois, precisamos instanciar o repositório que queremos testar e injetar nele a instância do mock.
Por fim, chamamos o método que estamos testando e fazemos as asserções sobre os resultados.
Você pode conferir os outros métodos de teste no nosso código-fonte.
Como verificar se um método assíncrono foi chamado?
A verificação não é assíncrona. Verify() inspeciona as chamadas que o Moq já registrou, então não há nada a aguardar nem um VerifyAsync() a procurar.
mockArticleRepository.Verify(x => x.GetByIdAsync(1), Times.Once);
O porém está na ordem. A chamada só é registrada depois que o código sob teste realmente a faz, então a chamada aguardada precisa terminar antes de verificarmos. Isso vale desde que o próprio método de teste seja async Task e todas as chamadas nele sejam aguardadas.
É em um teste async void que isso fica frágil. O xUnit.net 2.9 ainda espera que ele termine, mas o xUnit.net v3 não oferece mais suporte a testes async void. As orientações da Microsoft sobre programação assíncrona dizem: “Retorne ‘async void’ somente em manipuladores de eventos”, e citam a dificuldade de testar entre os motivos. Métodos de teste retornam Task, sempre.
It.IsAny<int>() corresponde a qualquer argumento quando o valor não importa, e Times.Once depois de duas chamadas é a asserção de um teste de cache: provar que a segunda chamada não chegou ao repositório costuma ser o objetivo.
Para se aprofundar nas asserções sobre a quantidade de chamadas, veja como usar o Moq para determinar se um método foi chamado.
| O método retorna | Configuração | Observações |
|---|---|---|
| Task<T> | .ReturnsAsync(value) | Tarefa concluída que encapsula o valor |
| Task<T>, valor por chamada | .ReturnsAsync((int id) => Find(id)) | A lambda recebe os argumentos da chamada |
| Task<T>, sequência de valores | .SetupSequence(...).ReturnsAsync(a).ReturnsAsync(b) | Um valor para cada chamada sucessiva |
| Task (sem resultado) | .Returns(Task.CompletedTask) | ReturnsAsync() não se aplica |
| ValueTask<T> | .ReturnsAsync(value) | Funciona da mesma forma |
| IAsyncEnumerable | .Returns(items.ToAsyncEnumerable()) | Não é aguardável. Sem ReturnsAsync; ToAsyncEnumerable() já vem no .NET 10, sem precisar de pacote |
| Lançar exceção de forma assíncrona | .ThrowsAsync(new NotFoundException()) | Coloca a tarefa em estado de falha em vez de lançar na chamada |
| Atraso antes de retornar | .ReturnsAsync(value, TimeSpan.FromMilliseconds(50)) | Para testes de tempo limite e cancelamento |
| Verificar se foi chamado | mock.Verify(x => x.GetByIdAsync(1), Times.Once) | Igual ao síncrono: Verify não é assíncrono |
Algo que vale a pena saber antes de escolher o Moq para um novo projeto: o SponsorLink foi incluído no Moq 4.20.0 (agosto de 2023) e removido na 4.20.2, então a versão atual, 4.21.0, está livre dele. O Moq não lançou nenhuma versão entre a 4.20.72, em setembro de 2024, e a 4.21.0, em setembro de 2026, o que convém considerar na escolha de uma biblioteca de mocks. O nosso guia sobre mocks eficazes com o NSubstitute cobre o mesmo assunto com uma biblioteca que continua sendo mantida ativamente.
Conclusão
Neste artigo, aprendemos a simular métodos assíncronos com o framework Moq, usando ReturnsAsync() para métodos que retornam Task<T> e Returns(Task.CompletedTask) para métodos que retornam uma Task simples. Vimos também como verificar se um método assíncrono foi de fato chamado e a armadilha de fazer isso em um teste async void.
Testado com o .NET 10.0.10.