I'm testing a WCF service methods using specflow and nunit; my scenarios look like the following:
Feature: GetAccount
Testing API method 'get account'
Background:
Given Server is running
Scenario: Succesful Get
Given An Existing Account
When I call the GetAccount API method With password = "123"
Then the result should be Success
I'm not sure on how to implement the background step;
The server can be run as console / windows service using Topshelf-
private static void Main()
{
Host host = HostFactory.New(config =>
{
config.Service<ServiceInitializer>(service =>
{
service.ConstructUsing(s => new ServiceInitializer());
service.WhenStarted((s, control) => s.Start(control));
service.WhenStopped((s, control) => s.Stop(control));
});
config.RunAsPrompt();
});
host.Run();
}
public class ServiceInitializer : ServiceControl
{
private readonly ILog m_log;
public ServiceInitializer()
{
log4net.Config.XmlConfigurator.Configure();
m_log = LogManager.GetLogger("Server");
}
public bool Start(HostControl hostControl)
{
try
{
var host = new IoCServiceHost(typeof(MyService));
host.Open();
m_log.Info("Server is now open.");
return true;
}
catch (Exception exception)
{
m_log.Fatal("Initialization of service failed",exception);
return false;
}
}
public bool Stop(HostControl hostControl)
{
m_log.Info("Server has closed");
return true;
}
}
should I just execute the .exe service file, or can I use my ServiceInitializer in some way? perhaps I could use nUnit's [SetUpFixture]?
Are there any Specflow best practices?
Let's consider what you want to test.
Do you need to test that Windows correctly runs services?
Do you need to test that Topshelf correctly starts services?
Or do you just want to test that GetAccount works?
I'll bet that you are using Topshelf to make your life easier, so do that and trust that their code works within windows. Its a valid assumption since there code will be used in many places and they probably have their own test suites, and if your assumption is wrong, then test it later when you find the problems.
So all you really need is
[BeforeFeature]
public void Background()
{
FeatureContext.Current["Host"] =new MyHostObject();
}
[When("I call GetAccount API method with password =\"(\.*)\"")]
public void WhenICallGetAccount(string password)
{
var host = (MyHostObject)FeatureContext.Current["Host"];
ScenarioContext.Current["Account"] = host.GetAccount(password);
}
[Then("the result should be success")]
public void ThenTheResultShouldBeSuccessful()
{
var account = (MyAccount)ScenarioContext.Current["Account"];
//assuming using Should;
account.ShouldNotBeNull();
}
Related
I'm using the dotnet Testcontainers library to spin up a SQL Server database in Docker for integration testing. I'm using the MSTest framework.
My idea is to run a TestInitialize where the container will start, populate the database and run assertions in the test method, and then run a TestCleanup at the end which will stop the container and dispose of it.
However, the container starts up in Docker and the test hangs (I guess it never ends up running for some reason). Also I'm not 100% sure how to populate the database (I couldn't find any commands for initialising and SQL scripts).
Here is the code:
[TestClass]
public class WithFixtureData
{
private static readonly TestcontainersContainer _dbContainer =
new TestcontainersBuilder<TestcontainersContainer>()
.WithImage("mcr.microsoft.com/mssql/server")
.WithEnvironment("Database", "Master")
.WithEnvironment("User Id", "SA")
.WithEnvironment("Password", "YourSTRONG!Passw0rd")
.WithCleanUp(true)
.Build();
[TestInitialize]
public async Task StartContainer()
{
await _dbContainer.StartAsync();
///container starts
}
[TestMethod]
public async Task ShouldBringCorrectFixturesBack()
{
///populate db and run assertions. This code never seems to run
}
[TestCleanup]
public async Task DisposeContainer()
{
await _dbContainer.StopAsync();
///this part of the code never seems to be executed either
}
}
I am wondering how the example above even runs, the configuration looks wrong. I attached a working example below, but first, lets take a look into the issues:
The environment variables do not exist. The mcr.microsoft.com/mssql/server image has the following environment variables (see section Environment Variables) to configure SQL Server on Linux containers. The section How to use this Image might also help. To run the container at least these configurations are necessary:
.WithEnvironment("ACCEPT_EULA", "Y")
.WithEnvironment("MSSQL_SA_PASSWORD", "yourStrong(!)Password")
The database port is not exposed, you cannot connect to the database. To expose and bind a container port to a random public host port use .WithPortBinding(1433, true). Here is another example that shows how to expose a container port.
Your configuration does not use a wait strategy to indicate readiness of the service running inside the container. A wait strategy for SQL Server could be something like:
.WithWaitStrategy(Wait.ForUnixContainer().UntilCommandIsCompleted("/opt/mssql-tools/bin/sqlcmd", "-S", "localhost,1433", "-U", "sa", "-P", "yourStrong(!)Password"))
A working configuration will look something like:
[TestClass]
public sealed class SO
{
private const string Database = "master";
private const string Username = "sa";
private const string Password = "yourStrong(!)Password";
private const ushort MssqlContainerPort = 1433;
private readonly TestcontainersContainer _dbContainer =
new TestcontainersBuilder<TestcontainersContainer>()
.WithImage("mcr.microsoft.com/mssql/server:2022-latest")
.WithPortBinding(MssqlContainerPort, true)
.WithEnvironment("ACCEPT_EULA", "Y")
.WithEnvironment("MSSQL_SA_PASSWORD", Password)
.WithWaitStrategy(Wait.ForUnixContainer().UntilCommandIsCompleted("/opt/mssql-tools/bin/sqlcmd", "-S", $"localhost,{MssqlContainerPort}", "-U", Username, "-P", Password))
.Build();
[TestInitialize]
public Task StartContainer()
{
return _dbContainer.StartAsync();
}
[TestCleanup]
public Task DisposeContainer()
{
return _dbContainer.StopAsync();
}
[TestMethod]
public Task Question_74323116()
{
var connectionString = $"Server={_dbContainer.Hostname},{_dbContainer.GetMappedPublicPort(MssqlContainerPort)};Database={Database};User Id={Username};Password={Password};";
using (var sqlConnection = new SqlConnection(connectionString))
{
try
{
sqlConnection.Open();
}
catch
{
Assert.Fail("Could not establish database connection.");
}
}
return Task.CompletedTask;
}
}
I have this in the ConfigureServices method:
services.AddHttpClient("ClientUsingCredentials")
.ConfigurePrimaryHttpMessageHandler(() =>
{
var credentials = new NetworkCredential("someUsername", "somePassword");
return new HttpClientHandler()
{
// UseDefaultCredentials = true,
Credentials = credentials
};
});
Now, my service looks like this:
public class WebAppService : IWebAppService
{
private readonly ILogger<WebAppService> _logger;
private readonly IHttpClientFactory _httpClientFactory;
public WebAppService(ILogger<WebAppService> logger, IHttpClientFactory httpClientFactory)
{
_logger = logger;
_httpClientFactory = httpClientFactory;
}
public async Task<WebAppReport> CheckWebSiteWithCredentialsAsync(string someUrlThatNeedsCredentials)
{
WebAppReport report = new();
try
{
using (var client = _httpClientFactory.CreateClient("ClientUsingCredentials"))
{
var result = await client.GetAsync(someUrlThatNeedsCredentials);
if (result.IsSuccessStatusCode)
{
// Do something with the result
report.IsCheckSuccessful = true;
}
else
{
// Set report fields accordingly
}
}
}
catch (Exception ex)
{
_logger.LogError(ex, $"Something went wrong while checking the website: '{someUrlThatNeedsCredentials}'.");
// Set report fields accordingly
}
return report;
}
}
The WebAppReport class:
public class WebAppReport
{
public bool IsCheckSuccessful { get; set; }
//Some other fields here
}
Is there a way to write unit test for this service method that also checks that it passes correct credentials while sending out requests?
Edit: I see #Nkosi's answer saying that Unit testing is not possible in this case. So, can you please show an example for doing integration test on it?
Should I be creating the service in my test method using new ServiceCollection(), add the service in the same way I did in ConfigureServices method, set correct credentials and so on? Would love to see your example on it.
Thank you!
Is there a way to write unit test for this method that also checks that it passes correct credentials while sending out requests?
Short answer: No. Not in a unit test.
That is because the ConfigurePrimaryHttpMessageHandler is a framework cross-cutting concern that is only applied by the actual HttpClientFactory at run time when creating the actual client that will be used to make the request.
You are basically trying to test that the HttpClientFactory implementation will behave as designed. Microsoft would have tested that before releasing it for use.
For your specific scenario, something like that would ideally need to be done in an integration test.
For testing your WebAppService you should focus on the logic local to the subject under test.
I have two tests of an ASP.NET core webapp within a single test class. The class uses an IClassFixture<WebApplicationFactory<Startup>>.
The first test is a happy path test: when a message arrives on a Kafka topic, some file appears in a particular output directory.
The second test is to verify that if something goes wrong with outputting said file, the webapp should then be in an unhealthy status.
For brevity, I'm omitting the code that publishes to Kafka and that generates the output folder the application saves files to - neither of these are relevant.
[CollectionDefinition("e2e", DisableParallelization = true)]
[Trait("Category", "IntegrationTest")]
public class EndToEndTest : IClassFixture<WebApplicationFactory<Startup>>, IClassFixture<KafkaFixture>
{
private readonly WebApplicationFactory<Startup> _factory;
public EndToEndTest(WebApplicationFactory<Startup> factory)
{
_factory = factory;
KafkaUtils.Publish(SomeMessage()).GetAwaiter().GetResult();
}
[Fact]
public void WhenXReceived_ThenFileIsOutput()
{
var options = OutputFolder();
Directory.CreateDirectory(options.Path);
RunService(services => services.AddSingleton(Options.Create(options))).CreateClient();
Thread.Sleep(10_000);
var outputDirectory = Directory.GetFiles(options.Path);
Assert.Single(outputDirectory);
var file = outputDirectory.Single();
Assert.NotEmpty(File.ReadAllLinesAsync(file).GetAwaiter().GetResult());
Directory.Delete(options.Path, true);
}
[Fact]
public void WhenFileTransferFails_ThenAppShouldBeUnhealthy()
{
var options = OutputFolder();
// only try once, so we don't have to wait
var retry = new RetryPolicyConfiguration {OnErrorRetryCount = 1};
var factory = RunService(services =>
{
services.AddSingleton(Options.Create(options));
services.AddSingleton(Options.Create(retry));
services.RemoveAll<IFileWriter>();
services.AddScoped<IFileWriter, ThrowingFileWriter>();
});
var client = factory.CreateClient();
Thread.Sleep(10_000);
Assert.False(Directory.Exists(options.Path));
var response = client.GetAsync("/health/ready").GetAwaiter().GetResult();
var stream = response.Content.ReadAsStreamAsync().GetAwaiter().GetResult();
using var reader = new StreamReader(stream);
var body = reader.ReadToEndAsync().GetAwaiter().GetResult();
var health = JsonConvert.DeserializeObject<HealthCheckResult>(body);
Assert.NotEqual(HealthStatus.Healthy, health.Status);
}
protected WebApplicationFactory<Startup> RunService(Action<IServiceCollection> serviceConfig)
{
return _factory
.WithWebHostBuilder(builder => builder
.ConfigureTestServices(services =>
{
serviceConfig(services);
services.AddSubscribeBus(new ConfigurationBuilder()
.AddJsonFile(ConfigMapFileProvider.FromRelativePath("config"),
"appsettings.e2e.json", true, true)
.Build());
}));
}
}
public class ThrowingFileWriter : IFileWriter
{
public void Write(string fileName, envelope envelope)
{
throw new Exception("foo");
}
public void Delete(string fileName)
{
}
}
If I run WhenFileTransferFails_ThenAppShouldBeUnhealthy() (unhealthyTest from here on, for brevity) on its own, then the test passes. But if I run the entire test class, then WhenXReceived_ThenFileIsOutput() (healthyTest, for brevity) runs before it, and for some reason unhealthyTest seems to use the service context that was created for healthyTest.
I've tried my best to work out what's going on, but it doesn't make sense to me. When I debug through it, the order of events is roughly as follows:
healthyTest starts
Startup invoked for healthytest
Constructor of service that uses IFileWriter is invoked with real instance of service
healthyTest completes
unhealthyTest starts
Constructor of service that uses IFileWriter is invoked with real instance of service (???)
Startup invoked for unhealthyTest
serviceConfig action invoked for unhealthyTest: all implementations of IFileWriter replaced with ThrowingFileWriter - but this is too late, it's already been constructed with the real instance in step 6
Consequently, the real service is used instead of the throwing service, and the exception handler that sets the app status to Unhealthy is never invoked.
Originally this was all running asynchronously, but I thought that perhaps the async nature of the tests meant that the two were conflicting with the webapp created by the factory - hence all the GetAwaiter().GetResult().
What am I doing wrong in my setup of the webapp for the test?
Note: it's unfortunately absolutely not an option to move these tests into their own test classes.
Having an issue with akka.net. I need to access an actor which I have already created with a specific name. I can retrieve the actor from IActorContext but I am struggling to access it from the ActorSystem.
I have created a method called GetOrCreateActor which attempts to get the actor using ActorSelection. If it doesn't exist, the catch creates a new actor with the name. If it does exist, I want it to return the reference. However, it never returns from '.Result'. Assuming this could be some sort of deadlocking issue.
public static IActorRef GetOrCreateActor<T>(this ActorSystem actorSystem, string actorPath, string name = null) where T : ActorBase
{
try
{
return actorSystem.ActorSelection(actorPath).ResolveOne(TimeSpan.FromSeconds(1)).Result;
}
catch
{
return actorSystem.ActorOf(actorSystem.DI().Props<T>(), name);
}
}
Edit
I've tried to include a simplified version of the calling code below.
The actor system is created in an IOC container using AutoFac (ExampleActor is the ReceiveActor I am trying to access):
containerBuilder.RegisterAssemblyTypes(typeof(ExampleActor).Assembly).Where(x => x.Name.EndsWith("Actor"));
var lazyContainer = new Lazy<IContainer>(() => containerBuilder.Build());
containerBuilder.Register(c =>
{
var system = ActorSystem.Create("ExampleActorSystem");
new AutoFacDependencyResolver(lazyContainer.Value, system);
return system;
}).As<ActorSystem>().SingleInstance();
return lazyContainer.Value;
ActorSystem is then injected into another class, where I call the GetOrCreateActor method (via the Execute method):
public class ExampleCommand : IExampleCommand
{
private readonly ActorSystem _actorSystem;
public ExampleCommand(ActorSystem actorSystem)
{
_actorSystem = actorSystem;
}
public void Execute()
{
SendMessage();
}
private void SendMessage()
{
string message = new Message();
_actorSystem.GetOrCreateActor<ExampleActor>("akka://ExampleActorSystem/user/ExampleActor", "ExampleActor").Tell(message);
}
}
The above command would be called from a RESTful endpoint
public ExampleGetModule(IExampleCommand exampleCommand)
{
Get["/api/createExample"] = parameters =>
{
exampleCommand.Execute();
};
}
Your deadlocking issue looks more like it has to do with how you're using your container than it does Akka.NET:
var lazyContainer = new Lazy<IContainer>(() => containerBuilder.Build());
containerBuilder.Register(c =>
{
var system = ActorSystem.Create("ExampleActorSystem");
new AutoFacDependencyResolver(lazyContainer.Value, system);
return system;
}).As<ActorSystem>().SingleInstance();
In terms of what can go wrong here, self-referential Lazy<T> types are an infamous source of race-conditions. You should not be calling lazyContainer.Value inside of this registration method if the output of containerBuilder.Build depends on the input of containerBuilder.Register.
Last thing is to use step-through debugging to make sure that your application actually calls into the ResolveOne method here - if you're not getting a timeout exception back then it means that your application is deadlocking on producing the actor system (because of how DI is configured).
The exact situation is I'm doing E2E tests with Protractor.NET (.NET port of AngularJS's Protractor E2E framework) and I would like to make some web requests (and the API -- System.Net.Http.HttpClient -- has all Async/Task methods) to Arrange my test before I Act/Assert, only I need to do this same Arrange-ing for several tests.
I'm using xUnit.net as my test runner they use an interface (IUseFixture<T>) for per-fixture setup code. It would be nice if there was a IAsyncUseFixture<T> that had a Task SetFixtureAsync(T t); or something. I don't think such a thing exists. Additionally I don't think constructors can use await either, and constructors are the only other way to execute the same block of code per-test in xUnit.net.
What are my options? .Result? Isn't that bad practice (deadlock)?
xUnit has an IAsyncLifetime interface for async setup/teardown. The methods you need to implement are Task InitializeAsync() and Task DisposeAsync().
InitializeAsync is called immediately after the class has been created, before it is used.
DisposeAsync is called just before IDisposable.Dispose if the class also implements IDisposable.
e.g.
public class MyTestFixture : IAsyncLifetime
{
private string someState;
public async Task InitializeAsync()
{
await Task.Run(() => someState = "Hello");
}
public Task DisposeAsync()
{
return Task.CompletedTask;
}
[Fact]
public void TestFoo()
{
Assert.Equal("Hello", someState);
}
}
I would use AsyncLazy
http://blog.stephencleary.com/2012/08/asynchronous-lazy-initialization.html
In my case I want to run some integration tests against a self hosted web api.
public class BaseTest()
{
private const string baseUrl = "http://mywebsite.web:9999";
private static readonly AsyncLazy<HttpSelfHostServer> server = new AsyncLazy<HttpSelfHostServer>(async () =>
{
try
{
Log.Information("Starting web server");
var config = new HttpSelfHostConfiguration(baseUrl);
new Startup()
.Using(config)
.Add.AttributeRoutes()
.Add.DefaultRoutes()
.Remove.XmlFormatter()
.Serilog()
.Autofac()
.EnsureInitialized();
var server = new HttpSelfHostServer(config);
await server.OpenAsync();
Log.Information("Web server started: {0}", baseUrl);
return server;
}
catch (Exception e)
{
Log.Error(e, "Unable to start web server");
throw;
}
});
public BaseTest()
{
server.Start()
}
}