Reputation: 142
I'm writing tests for my discord bot (using XUnit) and I want to know whether it's possible to replace my tests with one test. If so, how would I do that?
So far I wrote 4 unit tests for my Unity class (which is a wrapper for Unity Container, DI framework). These tests work as expected, but adding a new test everytime I add a new type to container just doesn't seem right. I looked at answers on similar questions, but solutions were either complex or not useful for my case.
My method from Unity class that is being tested:
public static T Resolve<T>()
{
return Container.Resolve<T>();
}
It returns an instance of corresponding type from Unity Container.
Tests:
[Fact]
public void ResolveIDataStorage_ShouldWork()
{
var storage1 = Unity.Resolve<IDataStorage>();
var storage2 = Unity.Resolve<IDataStorage>();
Assert.NotNull(storage1);
Assert.NotNull(storage2);
Assert.Same(storage1, storage2);
}
[Fact]
public void ResolveILogger_ShouldWork()
{
var logger1 = Unity.Resolve<ILogger>();
var logger2 = Unity.Resolve<ILogger>();
Assert.NotNull(logger1);
Assert.NotNull(logger2);
Assert.Same(logger1, logger2);
}
[Fact]
public void ResolveDiscordSocketClient_ShouldWork()
{
var client1 = Unity.Resolve<DiscordSocketClient>();
var client2 = Unity.Resolve<DiscordSocketClient>();
Assert.NotNull(client1);
Assert.NotNull(client2);
Assert.Same(client1, client2);
}
[Fact]
public void ResolveConnection_ShouldWork()
{
var con1 = Unity.Resolve<Connection>();
var con2 = Unity.Resolve<Connection>();
Assert.NotNull(con1);
Assert.NotNull(con2);
Assert.Same(con1, con2);
}
In each test I resolve some type and assert that two objects are not null and that they should be the same instance. Basically, these asserts should work for any type (or for a set of certain types, that could be parameters for [Theory] test), so, to avoid copy-pasting, it would be very convinient to have one test.
Upvotes: 0
Views: 1515
Reputation: 4219
If your goal is just to have one test that follows the same pattern for each type that you want to test, you could extract the test into its own generic method and just call that for each type within a single test:
[Fact]
public void Resolve_ShouldWork()
{
AssertResolvedTypesAreSame<IDataStorage>();
AssertResolvedTypesAreSame<ILogger>();
AssertResolvedTypesAreSame<DiscordSocketClient>();
AssertResolvedTypesAreSame<Connection>();
}
private void AssertResolvedTypesAreSame<T>()
{
var t1 = Unity.Resolve<T>();
var t2 = Unity.Resolve<T>();
Assert.NotNull(t1);
Assert.NotNull(t2);
Assert.Same(t1, t2);
}
Upvotes: 4
Reputation: 247443
Use reflection to make a generic method of the desired type to be resolved and invoke it.
From there assert the expected behavior.
Using inline data you can now repeat the test without duplicating code.
The following example is based on the already provided code.
[Theory]
[InlineData(typeof(IDataStorage))]
[InlineData(typeof(ILogger))]
[InlineData(typeof(DiscordSocketClient))]
[InlineData(typeof(Connection))]
public void Resolve_Singleton_Services_ShouldWork(Type type) {
//Arrange
var unityType = typeof(Unity);
var resolve = unityType.GetMethod("Resolve", BindingFlags.Static| BindingFlags.Public);
var genericResolve = resolve?.MakeGenericMethod(type);
//Act
var instance1 = genericResolve?.Invoke(null, null); // Unity.Resolve<type>()
var instance2 = genericResolve?.Invoke(null, null); // Unity.Resolve<type>()
//Assert
Assert.NotNull(instance1);
Assert.NotNull(instance2);
Assert.Same(instance1, instance2);
}
The arrange part of the test assumes that what every configuration for the container has already been invoked.
Upvotes: 2