O MSTest 4.4 permite executar projetos de teste usando o mesmo modelo de implantação que o aplicativo pode utilizar em produção, por meio da geração do código-fonte dos testes e da habilitação de Native AOT e da redução. A ideia não é substituir a execução gerenciada habitual dos testes, mas adicionar um fluxo nativo que revele problemas relacionados à compilação antecipada e à remoção de código não utilizado antes do lançamento do aplicativo.
Segundo Amaury Levé, Principal Software Engineer, usar MSTest.Sdk/4.4.0 com destino net10.0 e definir PublishAot=true ativa a geração de código-fonte do MSTest e o fluxo do arquivo executável nativo. O MSTest.Sdk usa a Microsoft Testing Platform, ou MTP, por padrão. Já os projetos que ainda usam VSTest devem consultar as orientações de migração para a MTP, pois os parâmetros de linha de comando, a integração com a CI e algumas entradas de .runsettings compatíveis são diferentes.
O que muda na prática?
Depois de configurar o projeto, os testes devem ser publicados para o mesmo sistema operacional e a mesma arquitetura almejados pelo aplicativo. A fonte apresenta um exemplo usando o identificador linux-x64, que pode ser substituído por valores como win-x64 ou osx-arm64. Depois da publicação, o arquivo executável resultante é executado diretamente, ou usando a extensão .exe no Windows.
Esse fluxo reduz a dependência da inspeção reflexiva abrangente do assembly, como a chamada Assembly.GetTypes(), e usa atributos e delegados gerados para criar e invocar os testes compatíveis. No entanto, a geração de código-fonte não significa ausência de Reflection; o modo padrão ReflectionFree mantém alguns caminhos reflexivos de fallback. Ao investigar problemas de compatibilidade, é possível usar MSTestSourceGenMode=Rooting para preservar os membros dos testes descobertos e continuar usando a execução reflexiva.
Um fluxo limitado de CI antes da expansão
A recomendação prática é manter a execução rápida dos testes gerenciados para o feedback diário e, em seguida, escolher um único projeto de testes diretamente relacionado ao fluxo de implantação e adicionar um experimento de Native AOT na CI. Os dois fluxos devem ser comparados em três aspectos claros:
- Descobrir exatamente o mesmo número de testes.
- Obter os mesmos resultados para os testes.
- Registrar separadamente o tempo de publicação e execução nativa do tempo de execução dos testes.
É preferível começar o experimento em uma tarefa agendada ou em uma etapa de validação da versão e só movê-lo para cada pull request se o sinal fornecido justificar o custo adicional de publicação e execução. Também deve ser escolhido um projeto que teste fluxos sensíveis à implantação, como serialização, injeção de dependências, vinculação de configurações, extensões dependentes de Reflection ou uma biblioteca cuja compatibilidade com Native AOT as equipes precisem comprovar. Já um projeto que contenha apenas testes computacionais simples não fornecerá muitas evidências sobre a prontidão real do aplicativo.
Limitações que devem ser transformadas em gates de aceitação
Parte dos testes pode ser executada e o processo ser concluído com sucesso mesmo sem algumas classes serem registradas. Isso ocorre, por exemplo, quando uma classe de teste herda o atributo [TestClass] em vez de declará-lo diretamente, ou quando a classe não está disponível, é local ao arquivo, estática, pública aberta ou abstrata. A fonte indica que o diagnóstico MSTEST0069 ajuda a revelar um desses casos. Portanto, a correspondência do número de testes deve ser uma condição de lançamento, não uma observação que possa ser ignorada.
Outras limitações incluem métodos de teste públicos ou que usam parâmetros ref, out ou in, além da ausência de suporte a alguns padrões de fixtures de assembly por meio de [AssemblyFixtureProvider]. Algumas integrações do MSTest SDK, extensões da MTP e relatórios de CI também não estão disponíveis no fluxo de Native AOT, enquanto o suporte a TRX e Code Coverage continua disponível de acordo com o artigo. Por isso, os avisos do analisador e os diagnósticos de compilação devem ser tratados como gates de transição, não como avisos a serem silenciados.
Por que essa abordagem é importante?
Os testes gerenciados e o fluxo de Native AOT respondem a duas perguntas diferentes: o primeiro acelera o ciclo de desenvolvimento, enquanto o segundo verifica se os próprios testes conseguem funcionar dentro do modelo de implantação que o aplicativo utilizará. Isso não equivale a um teste abrangente do artefato final de produção; as configurações, o sistema operacional, a arquitetura, os serviços externos e o empacotamento podem ser diferentes. Mas remove uma variável importante: a diferença entre o modelo de redução e compilação antecipada usado pelo teste e pelo aplicativo.
A melhoria de desempenho não é garantida e não deve ser a justificativa principal. O tempo de descoberta e de inicialização pode diminuir graças à geração de código-fonte, mas o tempo de execução, o início do processo, a publicação e o Reflection restante podem dominar a duração total. A leitura editorial aqui é que o principal valor do experimento é aumentar a correspondência entre o teste e a implantação, mesmo que o ganho de velocidade seja limitado. A abordagem também pode ser revertida: se o custo do fluxo superar a confiança que ele acrescenta, sua frequência pode ser reduzida, o projeto escolhido pode ser alterado ou o experimento pode ser interrompido sem desativar o conjunto de testes gerenciados.