1
0
Форкнуть 0

make functional tests pass all the time

This commit is contained in:
Sergey Kanzhelev 2015-05-08 19:53:22 -07:00
Родитель 06f64609d8
Коммит ba1ae3d3ec
11 изменённых файлов: 190 добавлений и 248 удалений

Просмотреть файл

@ -1,19 +1,37 @@

namespace FunctionalTestUtils
{
using Microsoft.ApplicationInsights.Channel;
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using Microsoft.ApplicationInsights.Channel;
internal class BackTelemetryChannel : ITelemetryChannel
public class BackTelemetryChannel : ITelemetryChannel
{
internal IList<ITelemetry> buffer;
private IList<ITelemetry> buffer;
public BackTelemetryChannel()
{
this.buffer = new List<ITelemetry>();
}
public bool DeveloperMode { get; set; }
public IList<ITelemetry> Buffer
{
get
{
return this.buffer;
}
}
public bool DeveloperMode
{
get
{
return true;
}
set
{
}
}
public string EndpointAddress
{
@ -38,11 +56,8 @@ namespace FunctionalTestUtils
}
public void Send(ITelemetry item)
{
if (this.buffer != null)
{
this.buffer.Add(item);
}
}
}
}

Просмотреть файл

@ -1,40 +0,0 @@
namespace FunctionalTestUtils
{
using System.Collections.Generic;
using Microsoft.ApplicationInsights.Channel;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.AspNet.Builder;
using Microsoft.Framework.DependencyInjection;
public static class BackTelemetryChannelExtensions
{
/// <summary>
/// I know, statics are bad. Let's refactor when it will become a problem
/// </summary>
private static BackTelemetryChannel channel;
/// <summary>
/// If applciaiton is running under functional tests - in process channel will be used.
/// Otherwise - regular channel will be used
/// </summary>
/// <param name="services"></param>
public static void UseFunctionalTestTelemetryChannel(this IApplicationBuilder app)
{
channel = new BackTelemetryChannel();
var telemetryConfiguration = app.ApplicationServices.GetRequiredService<TelemetryConfiguration>();
telemetryConfiguration.TelemetryChannel = channel;
}
/// <summary>
/// I haven't implemented Reset method to delete channel. Since an applicaiton will either be used by
/// unit tests or started directly - it should not be a big problem, every unit test will override
/// channel with it's own version.
/// </summary>
/// <param name="buffer"></param>
public static void InitializeFunctionalTestTelemetryChannel(IList<ITelemetry> buffer)
{
channel.buffer = buffer;
}
}
}

Просмотреть файл

@ -1,19 +1,34 @@
namespace FunctionalTestUtils
{
using System;
using Microsoft.ApplicationInsights.Channel;
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Server.WebListener;
using Microsoft.Framework.ConfigurationModel;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Logging;
public class InProcessServer : IDisposable
{
private static Random random = new Random();
private IDisposable hostingEngine;
private string url = "http://localhost:" + (new Random(239).Next(5000, 8000)).ToString();
private string url;
private readonly BackTelemetryChannel backChannel;
public BackTelemetryChannel BackChannel
{
get
{
return this.backChannel;
}
}
public InProcessServer(string assemblyName)
{
this.Start(assemblyName);
this.url = "http://localhost:" + random.Next(5000, 14000).ToString();
this.backChannel = this.Start(assemblyName);
}
public string BaseHost
@ -24,9 +39,9 @@
}
}
private void Start(string assemblyName)
private BackTelemetryChannel Start(string assemblyName)
{
var customConfig = new NameValueConfigurationSource();
var customConfig = new MemoryConfigurationSource();
customConfig.Set("server.urls", this.BaseHost);
var config = new Configuration();
config.Add(customConfig);
@ -39,6 +54,8 @@
};
this.hostingEngine = new HostingEngine().Start(context);
return (BackTelemetryChannel)context.ApplicationServices.GetService<ITelemetryChannel>();
}
public void Dispose()

Просмотреть файл

@ -1,35 +0,0 @@
namespace FunctionalTestUtils
{
using System;
using System.Collections.Generic;
using Microsoft.Framework.ConfigurationModel;
public class NameValueConfigurationSource : IConfigurationSource
{
private Dictionary<string, string> _config = new Dictionary<string, string>();
public NameValueConfigurationSource()
{
}
public void Load()
{
//should already be loaded
}
public IEnumerable<string> ProduceSubKeys(IEnumerable<string> earlierKeys, string prefix, string delimiter)
{
throw new NotImplementedException();
}
public void Set(string key, string value)
{
_config.Add(key, value);
}
public bool TryGet(string key, out string value)
{
return _config.TryGetValue(key, out value);
}
}
}

Просмотреть файл

@ -1,77 +1,46 @@
namespace FunctionalTestUtils
{
using System;
using System.Diagnostics;
using System.Linq;
using System.Collections.Generic;
using System.Net.Http;
using Microsoft.ApplicationInsights.Channel;
using Microsoft.ApplicationInsights.DataContracts;
using Xunit;
public abstract class TelemetryTestsBase : IDisposable
public abstract class TelemetryTestsBase
{
protected const int TestTimeoutMs = 5000;
private InProcessServer server;
private IList<ITelemetry> buffer = new List<ITelemetry>();
private HttpClient client = new HttpClient();
public TelemetryTestsBase(string assemblyName)
{
this.server = new InProcessServer(assemblyName);
BackTelemetryChannelExtensions.InitializeFunctionalTestTelemetryChannel(buffer);
}
public IList<ITelemetry> Buffer
{
get
{
return this.buffer;
}
}
public InProcessServer Server
{
get
{
return this.server;
}
}
public HttpClient HttpClient
{
get
{
return this.client;
}
}
public void ValidateBasicRequest(string requestPath, RequestTelemetry expected)
public void ValidateBasicRequest(InProcessServer server, string requestPath, RequestTelemetry expected)
{
DateTimeOffset testStart = DateTimeOffset.Now;
var task = this.HttpClient.GetAsync(this.Server.BaseHost + requestPath);
var timer = Stopwatch.StartNew();
var httpClient = new HttpClient();
var task = httpClient.GetAsync(server.BaseHost + requestPath);
task.Wait(TestTimeoutMs);
var result = task.Result;
var actual = this.Buffer.OfType<RequestTelemetry>().Single();
var actual = server.BackChannel.Buffer.OfType<RequestTelemetry>().Single();
timer.Stop();
Assert.Equal(expected.ResponseCode, actual.ResponseCode);
Assert.Equal(expected.Name, actual.Name);
Assert.Equal(expected.Success, actual.Success);
Assert.Equal(expected.Url, actual.Url);
Assert.InRange<DateTimeOffset>(actual.Timestamp, testStart, DateTimeOffset.Now);
Assert.True(actual.Duration < (DateTimeOffset.Now - testStart), "duration");
Assert.True(actual.Duration < timer.Elapsed, "duration");
Assert.Equal(expected.HttpMethod, actual.HttpMethod);
}
public void ValidateBasicException(string requestPath, ExceptionTelemetry expected)
public void ValidateBasicException(InProcessServer server, string requestPath, ExceptionTelemetry expected)
{
DateTimeOffset testStart = DateTimeOffset.Now;
var task = this.HttpClient.GetAsync(this.Server.BaseHost + requestPath);
var httpClient = new HttpClient();
var task = httpClient.GetAsync(server.BaseHost + requestPath);
task.Wait(TestTimeoutMs);
var result = task.Result;
var actual = this.Buffer.OfType<ExceptionTelemetry>().Single();
var actual = server.BackChannel.Buffer.OfType<ExceptionTelemetry>().Single();
Assert.Equal(expected.Exception.GetType(), actual.Exception.GetType());
Assert.NotEmpty(actual.Exception.StackTrace);
@ -79,18 +48,5 @@
Assert.NotEmpty(actual.Context.Operation.Name);
Assert.NotEmpty(actual.Context.Operation.Id);
}
public void Dispose()
{
if (this.server != null)
{
this.server.Dispose();
}
if (this.client != null)
{
this.client.Dispose();
}
}
}
}

Просмотреть файл

@ -1,18 +1,18 @@
namespace SampleWebAppIntegration.FunctionalTest
{
using System;
using System.Linq;
using FunctionalTestUtils;
using Microsoft.ApplicationInsights.DataContracts;
using Xunit;
public class ExceptionTelemetryTests : TelemetryTestsBase
{
public ExceptionTelemetryTests() : base("Mvc6Framework45.FunctionalTests")
{ }
private const string assemblyName = "Mvc6Framework45.FunctionalTests";
[Fact]
public void TestBasicRequestPropertiesAfterRequestingControllerThatThrows()
{
using (var server = new InProcessServer(assemblyName))
{
const string RequestPath = "/Home/Exception";
@ -21,18 +21,22 @@
expectedRequestTelemetry.Name = "GET Home/Exception";
expectedRequestTelemetry.ResponseCode = "500";
expectedRequestTelemetry.Success = false;
expectedRequestTelemetry.Url = new System.Uri(this.Server.BaseHost + RequestPath);
this.ValidateBasicRequest("/Home/Exception", expectedRequestTelemetry);
expectedRequestTelemetry.Url = new System.Uri(server.BaseHost + RequestPath);
this.ValidateBasicRequest(server, "/Home/Exception", expectedRequestTelemetry);
}
}
[Fact]
public void TestBasicExceptionPropertiesAfterRequestingControllerThatThrows()
{
using (var server = new InProcessServer(assemblyName))
{
var expectedExceptionTelemetry = new ExceptionTelemetry();
expectedExceptionTelemetry.HandledAt = ExceptionHandledAt.Platform;
expectedExceptionTelemetry.Exception = new InvalidOperationException();
this.ValidateBasicException("/Home/Exception", expectedExceptionTelemetry);
this.ValidateBasicException(server, "/Home/Exception", expectedExceptionTelemetry);
}
}
}
}

Просмотреть файл

@ -1,17 +1,19 @@
namespace SampleWebAppIntegration.FunctionalTest
{
using System.Linq;
using System.Net.Http;
using FunctionalTestUtils;
using Microsoft.ApplicationInsights.DataContracts;
using System.Linq;
using Xunit;
public class RequestTelemetryTests : TelemetryTestsBase
{
public RequestTelemetryTests() : base("Mvc6Framework45.FunctionalTests")
{ }
private const string assemblyName = "Mvc6Framework45.FunctionalTests";
[Fact]
public void TestBasicRequestPropertiesAfterRequestingHomeController()
{
using (var server = new InProcessServer(assemblyName))
{
const string RequestPath = "/";
@ -20,13 +22,16 @@
expectedRequestTelemetry.Name = "GET Home/Index";
expectedRequestTelemetry.ResponseCode = "200";
expectedRequestTelemetry.Success = true;
expectedRequestTelemetry.Url = new System.Uri(this.Server.BaseHost + RequestPath);
expectedRequestTelemetry.Url = new System.Uri(server.BaseHost + RequestPath);
this.ValidateBasicRequest(RequestPath, expectedRequestTelemetry);
this.ValidateBasicRequest(server, RequestPath, expectedRequestTelemetry);
}
}
[Fact]
public void TestBasicRequestPropertiesAfterRequestingActionWithParameter()
{
using (var server = new InProcessServer(assemblyName))
{
const string RequestPath = "/Home/About/5";
@ -35,13 +40,16 @@
expectedRequestTelemetry.Name = "GET Home/About [id]";
expectedRequestTelemetry.ResponseCode = "200";
expectedRequestTelemetry.Success = true;
expectedRequestTelemetry.Url = new System.Uri(this.Server.BaseHost + RequestPath);
expectedRequestTelemetry.Url = new System.Uri(server.BaseHost + RequestPath);
this.ValidateBasicRequest(RequestPath, expectedRequestTelemetry);
this.ValidateBasicRequest(server, RequestPath, expectedRequestTelemetry);
}
}
[Fact]
public void TestBasicRequestPropertiesAfterRequestingNotExistingController()
{
using (var server = new InProcessServer(assemblyName))
{
const string RequestPath = "/not/existing/controller";
@ -50,23 +58,27 @@
expectedRequestTelemetry.Name = "GET /not/existing/controller";
expectedRequestTelemetry.ResponseCode = "404";
expectedRequestTelemetry.Success = false;
expectedRequestTelemetry.Url = new System.Uri(this.Server.BaseHost + RequestPath);
expectedRequestTelemetry.Url = new System.Uri(server.BaseHost + RequestPath);
this.ValidateBasicRequest(RequestPath, expectedRequestTelemetry);
this.ValidateBasicRequest(server, RequestPath, expectedRequestTelemetry);
}
}
[Fact]
public void TestMixedTelemetryItemsReceived()
{
var task = this.HttpClient.GetAsync(this.Server.BaseHost + "/home/contact");
using (var server = new InProcessServer(assemblyName))
{
var httpClient = new HttpClient();
var task = httpClient.GetAsync(server.BaseHost + "/home/contact");
task.Wait(TestTimeoutMs);
var request = this.Buffer.OfType<RequestTelemetry>().Single();
var eventTelemetry = this.Buffer.OfType<EventTelemetry>().Single();
var metricTelemetry = this.Buffer.OfType<MetricTelemetry>().Single();
var traceTelemetry = this.Buffer.OfType<TraceTelemetry>().Single();
var request = server.BackChannel.Buffer.OfType<RequestTelemetry>().Single();
var eventTelemetry = server.BackChannel.Buffer.OfType<EventTelemetry>().Single();
var metricTelemetry = server.BackChannel.Buffer.OfType<MetricTelemetry>().Single();
var traceTelemetry = server.BackChannel.Buffer.OfType<TraceTelemetry>().Single();
Assert.Equal(4, this.Buffer.Count);
Assert.Equal(4, server.BackChannel.Buffer.Count);
Assert.NotNull(request);
Assert.NotNull(eventTelemetry);
Assert.NotNull(metricTelemetry);
@ -74,3 +86,4 @@
}
}
}
}

Просмотреть файл

@ -23,6 +23,7 @@ using Microsoft.Framework.Runtime;
using Mvc6Framework45.FunctionalTests.Models;
using Microsoft.ApplicationInsights.AspNet;
using FunctionalTestUtils;
using Microsoft.ApplicationInsights.Channel;
namespace Mvc6Framework45.FunctionalTests
{
@ -52,7 +53,7 @@ namespace Mvc6Framework45.FunctionalTests
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add Application Insights services to the services container.
services.AddInstance<ITelemetryChannel>(new BackTelemetryChannel());
services.AddApplicationInsightsTelemetry(Configuration);
// Add Application settings to the services container.
@ -100,8 +101,6 @@ namespace Mvc6Framework45.FunctionalTests
// Add the console logger.
loggerfactory.AddConsole(minLevel: LogLevel.Warning);
app.UseFunctionalTestTelemetryChannel();
// Add Application Insights monitoring to the request pipeline as a very first middleware.
app.UseApplicationInsightsRequestTelemetry();
// Add the following to the request pipeline only in development environment.

Просмотреть файл

@ -1,18 +1,21 @@
namespace SampleWebAppIntegration.FunctionalTest
{
using System;
using System.Linq;
using FunctionalTestUtils;
using Microsoft.ApplicationInsights.DataContracts;
using Xunit;
public class ExceptionTelemetryTests : TelemetryTestsBase
{
public ExceptionTelemetryTests() : base("WebApiShimFw46.FunctionalTests")
private const string assemblyName = "WebApiShimFw46.FunctionalTests";
public ExceptionTelemetryTests()
{ }
[Fact]
public void TestBasicRequestPropertiesAfterRequestingControllerThatThrows()
{
using (var server = new InProcessServer(assemblyName))
{
const string RequestPath = "/api/exception";
@ -23,19 +26,23 @@
//that will set appropriate status code
expectedRequestTelemetry.ResponseCode = "200";
expectedRequestTelemetry.Success = true;
expectedRequestTelemetry.Url = new System.Uri(this.Server.BaseHost + RequestPath);
expectedRequestTelemetry.Url = new System.Uri(server.BaseHost + RequestPath);
this.ValidateBasicRequest(RequestPath, expectedRequestTelemetry);
this.ValidateBasicRequest(server, RequestPath, expectedRequestTelemetry);
}
}
[Fact]
public void TestBasicExceptionPropertiesAfterRequestingControllerThatThrows()
{
using (var server = new InProcessServer(assemblyName))
{
var expectedExceptionTelemetry = new ExceptionTelemetry();
expectedExceptionTelemetry.HandledAt = ExceptionHandledAt.Platform;
expectedExceptionTelemetry.Exception = new InvalidOperationException();
this.ValidateBasicException("/api/exception", expectedExceptionTelemetry);
this.ValidateBasicException(server, "/api/exception", expectedExceptionTelemetry);
}
}
}
}

Просмотреть файл

@ -2,16 +2,16 @@
{
using FunctionalTestUtils;
using Microsoft.ApplicationInsights.DataContracts;
using System.Linq;
using Xunit;
public class RequestTelemetryTests : TelemetryTestsBase
{
public RequestTelemetryTests() : base("WebApiShimFw46.FunctionalTests")
{ }
private const string assemblyName = "WebApiShimFw46.FunctionalTests";
[Fact]
public void TestBasicRequestPropertiesAfterRequestingValuesController()
{
using (var server = new InProcessServer(assemblyName))
{
const string RequestPath = "/api/values";
@ -20,13 +20,16 @@
expectedRequestTelemetry.Name = "GET Values/Get";
expectedRequestTelemetry.ResponseCode = "200";
expectedRequestTelemetry.Success = true;
expectedRequestTelemetry.Url = new System.Uri(this.Server.BaseHost + RequestPath);
expectedRequestTelemetry.Url = new System.Uri(server.BaseHost + RequestPath);
this.ValidateBasicRequest(RequestPath, expectedRequestTelemetry);
this.ValidateBasicRequest(server, RequestPath, expectedRequestTelemetry);
}
}
[Fact]
public void TestBasicRequestPropertiesAfterRequestingNotExistingController()
{
using (var server = new InProcessServer(assemblyName))
{
const string RequestPath = "/api/notexistingcontroller";
@ -35,13 +38,16 @@
expectedRequestTelemetry.Name = "GET /api/notexistingcontroller";
expectedRequestTelemetry.ResponseCode = "404";
expectedRequestTelemetry.Success = false;
expectedRequestTelemetry.Url = new System.Uri(this.Server.BaseHost + RequestPath);
expectedRequestTelemetry.Url = new System.Uri(server.BaseHost + RequestPath);
this.ValidateBasicRequest(RequestPath, expectedRequestTelemetry);
this.ValidateBasicRequest(server, RequestPath, expectedRequestTelemetry);
}
}
[Fact]
public void TestBasicRequestPropertiesAfterRequestingWebApiShimRoute()
{
using (var server = new InProcessServer(assemblyName))
{
const string RequestPath = "/api/values/1";
@ -50,10 +56,11 @@
expectedRequestTelemetry.Name = "GET Values/Get [id]";
expectedRequestTelemetry.ResponseCode = "200";
expectedRequestTelemetry.Success = true;
expectedRequestTelemetry.Url = new System.Uri(this.Server.BaseHost + RequestPath);
expectedRequestTelemetry.Url = new System.Uri(server.BaseHost + RequestPath);
this.ValidateBasicRequest(RequestPath, expectedRequestTelemetry);
this.ValidateBasicRequest(server, RequestPath, expectedRequestTelemetry);
}
}
}
}
}
}

Просмотреть файл

@ -4,6 +4,7 @@ using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting;
using Microsoft.Framework.ConfigurationModel;
using Microsoft.Framework.DependencyInjection;
using Microsoft.ApplicationInsights.Channel;
namespace SampleWebAPIIntegration
{
@ -23,7 +24,7 @@ namespace SampleWebAPIIntegration
// Use this method to add services to the container
public void ConfigureServices(IServiceCollection services)
{
// Add Application Insights services to the services container.
services.AddInstance<ITelemetryChannel>(new BackTelemetryChannel());
services.AddApplicationInsightsTelemetry(Configuration);
services.AddMvc();
@ -35,8 +36,6 @@ namespace SampleWebAPIIntegration
// Configure is called after ConfigureServices is called.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseFunctionalTestTelemetryChannel();
// Add Application Insights monitoring to the request pipeline as a very first middleware.
app.UseApplicationInsightsRequestTelemetry();