La bénéfice tiré des tests automatisés dans le cycle de vie d'une application n'est pas débattre. Ils peuvent se diviser en plusieurs groupes et chacun possède ses caractéristiques, avantages et inconvénients. Ils présentent néanmoins tous un point commun: l'implémentation et la maintenance de ces tests a un coût difficile à ignorer. Cette session présentera une autre approche complémentaire des tests : l'approche comportementale ou comment un seul test peut permettre de couvrir de bout en bout un scénario complet de l'application. Nous verrons un exemple d'implémentation de cette approche avec ASP.NET Core et SpecFlow.
5. Unit testing
« Une unité représente la plus petite partie testable d’une application »
• Séparation des responsabilités
• Isolation des couches
• Approche par contrat
Mocks / Stubs / Tests unitaires = CODE =
9. Behavior testing
« Valider un scénario fonctionnel sans dépendance aux détails
d’implémentation »
• Ecrire le minimum de code pour tester le maximum d’un scenario
fonctionnel
• Couvrir facilement un scénario de bout en bout
• Abstraction des dépendances externes
11. ASP.NET Core et Behavior testing
Startup
Standard Configuration Service Test Configuration Service
WebApp TestServer
WebHost
12. ASP.NET Core et Behavior testing
public class TestStartupConfigurationService : IStartupConfigurationService
{
public virtual void ConfigureEnvironment(IHostingEnvironment env)
{
env.EnvironmentName = "Test"
}
public virtual void ConfigureServices(IServiceCollection services, IConfigurationRoot configuration)
{
var connectionStringBuilder = new SqliteConnectionStringBuilder { DataSource = ":memory:" };
var connectionString = connectionStringBuilder.ToString();
var connection = new SqliteConnection(connectionString);
services.AddDbContext<DbContext>(options => options.UseSqlite(connection));
}
public virtual void Configure(IApplicationBuilder app, IHostingEnvironment env, …)
{
…
dbContext.Database.EnsureCreated();
…
}
}
Exemple - TestStartupConfigurationService :
13. ASP.NET Core et Behavior testing
Exemple - StandardStartupConfigurationService :
public class StartupConfigurationService : IStartupConfigurationService
{
public virtual void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{ }
public virtual void ConfigureEnvironment(IHostingEnvironment env)
{ }
public virtual void ConfigureService(IServiceCollection services, IConfigurationRoot configuration)
{
services.AddDbContext<DbContext>(options => options.UseSqlServer(@"[SQL_CONNECTION_STRING]"));
}
}
14. ASP.NET Core et Behavior testing
new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.ConfigureServices(s => s.AddSingleton<IStartupConfigurationService,
StartupConfigurationService>())
.Build()
.Run();
Exemple – Program.Main() :
15. ASP.NET Core et Behavior testing
public class Startup
{
private IStartupConfigurationService externalStartupConfiguration;
public Startup(IHostingEnvironment env, IStartupConfigurationService externalStartupConfiguration)
{
this.externalStartupConfiguration = externalStartupConfiguration;
this.externalStartupConfiguration.ConfigureEnvironment(env);
…
}
public void ConfigureServices(IServiceCollection services)
{
…
this.externalStartupConfiguration.ConfigureServices(services, Configuration);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
this.externalStartupConfiguration.Configure(app, env, loggerFactory, Configuration);
…
}
}
Exemple – Startup :
16. ASP.NET Core et Behavior testing
Exemple – TestMethod :
[TestMethod]
public void TestScenario()
{
Data[] data = CreateTestScenarioDataIntoInMemoryDb();
var webHostBuilder = new WebHostBuilder();
webHostBuilder.ConfigureServices(
s => s.AddSingleton<IStartupConfigurationService, TestStartupConfigurationService<DbContext>> ());
webHostBuilder.UseStartup<Startup>();
var testServer = new TestServer(webHostBuilder);
var response = testServer.CreateClient().GetAsync("/api/data").Result;
response.EnsureSuccessStatusCode();
var result = response.Content.ReadAsAsync<Data[]>().Result;
Assert.AreEqual(data.Length, result.Length);
for (int i = 0; i < data.Length; i++)
{
Assert.AreEqual(data[i], result[i]);
}
}
17. ASP.NET Core et Behavior testing
SpecFlow
• Description naturelle d’un scénario
• Abstraction du contexte technique
• Semi-intégration à NET Core 2.0
18. ASP.NET Core et Behavior testing
Feature: Get blogs
In order to list blogs
I want to get the list of blogs
Background:
Given A configured environment
Scenario: Get blogs should be ok
Given the following blogs
| Url |
| http://blog1.io |
| http://blog2.io |
When I get the list of blogs from Api
Then the result must be the following list
| Url |
| http://blog1.io |
| http://blog2.io |
[Binding]
public class MainSteps
{
[Given(@"A configured environment")]
public void GivenAWorkingEnvironment()
{}
[Given(@"the following blogs")]
public void GivenTheFollowingBlogs(Table table)
{}
[When(@"I get the list of blogs from Api")]
public void WhenIGetTheListOfBlogs()
{}
[Then(@"the result must be the following list")]
public void ThenTheResultMustBeTheFollowingList(Table
expectedResult)
{}
}
SpecFlow
19. ASP.NET Core et Behavior testing
Aller plus loin – Test de la stack MVC
• Gestion du ViewModel
• Gestion de l’Anti Forgery Token
• Gestion des dépendances Razor (.deps.json)
20. ASP.NET Core et Behavior testing
Aller plus loin – Gestion de l’identité
• Développement de son propre AuthenticationScheme
AuthenticationSchemeOptions
Définition des informations de l’identité de test
AuthenticationHandler<SchemeOptions>
Création de l’identité de test
AuthenticationBuilder
Création du schéma d’authentification à partir des options et du handler
21. ASP.NET Core et Behavior testing
• Série d’article sur les tests comportementaux avec ASP.NET Core
http://geeklearning.io/a-different-approach-to-test-your-asp-net-core-
application/
• Projet GitHub
https://github.com/geeklearningio/Testavior
• Packages nuget
https://www.nuget.org/packages/GeekLearning.Testavior
https://www.nuget.org/packages/GeekLearning.Testavior.Configuration
Notes de l'éditeur
Moreover, in this case your tests are very dependent of the implementation details so if a detail has changed, you are likely to have to update the tests as well. Doing so is costly and might also lead to new defects.
Webhostbuilder: configure et instantie un hote web responsable de démarrer l’application et de gérer son cycle de vie
Startup: configure les services et la request pipeline
Anti Forgery Token vise à protéger les users des attaques de type CSRF (Cross Site Request Forgery). Un attaquant envoie un mail à une victime contenant un lien vers un formulaire web d’un site malicieux. Le formulaire est configure pour un POST sur le site vulnerable, les données seraient donc envoyées sur le site vulnerable avec les autorisations de la victime.
Pour se protéger, lors du GET du form, le serveur inclu 2 tokens, 1 dans un cookie et l’autre comme input hidden dans le formulaire. Si le client n’envoie pas les 2 tokens, la requête est invalidée. Cela fonctionne car le site malicieux ne peut pas lire le token renvoyé par le serveur grâce à la politique de sécurité de l’origine identique. Le site attaquant peut envoyer un POST mais pas lire la réponse.
The Razor engine uses dependency files (.deps.json) to resolve some references at runtime. So in order to test the MVC part of a application, it is necessary to import these files. To do it, add the following section to your .csproj