I am kind of learning to write unit test cases and I am using Xunit framework. I have a scenario where I would like to write a test case to test different scenario in my cosmos db emulator. To do that I am trying to create a database, container and insert few test data in my cosmos db emulator and then write my facts and also delete it once test cases are completed...below is the code which I figured out from internet, would like to know if I am doing it correctly... and where can I start writing my test cases.
namespace Project.Tests
{
public class DatabaseFixture : IDisposable
{
private static readonly string CosmosEndpoint = "https://localhost:8081";
private static readonly string EmulatorKey = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
private static readonly string DatabaseId = "Recordings";
private static readonly string RecordingCollection = "testdata";
public DatabaseFixture()
{
var client = new DocumentClient( new Uri( CosmosEndpoint ), EmulatorKey,
new ConnectionPolicy
{
ConnectionMode = ConnectionMode.Direct,
ConnectionProtocol = Protocol.Tcp
} );
var databaseCreationResult = client.CreateDatabaseAsync( new Database { Id = DatabaseId } ).Result;
var collectionCreationResult = client.CreateDocumentCollectionAsync( UriFactory.CreateDatabaseUri( DatabaseId ),
new DocumentCollection { Id = RecordingCollection } ).Result;
var recData = new Recordings { Id = "Test" };
var itemResult = client
.CreateDocumentAsync(
UriFactory.CreateDocumentCollectionUri( DatabaseId, RecordingCollection ), recData )
.Result;
var document = client
.ReadDocumentAsync(
UriFactory.CreateDocumentUri( DatabaseId, RecordingCollection, itemResult.Resource.Id ) )
.Result;
Recordings site = (dynamic)document.Resource;
}
public void Dispose()
{
// ... clean up test data from the database ...
throw new NotImplementedException();
}
}
public class Recordings
{
public string Id { get; set; }
}
public class MyDatabaseTests : IClassFixture<DatabaseFixture>
{
DatabaseFixture fixture;
public MyDatabaseTests( DatabaseFixture fixture )
{
this.fixture = fixture;
}
// ... write tests, using fixture.Db to get access to the database server ...
}
}
Be careful that using a web API is not really part of the Unit Test philosophy. A Unit Test is usually expected to be independent from external interaction.
You can still use xUnit to peform your testing, but you are not in a UNIT test context.
If you have access to the code behind the service, you could Unit Test it without the Web layer. (as an exemple, you can Unit test directly the REST controller class.)
If you ignore this point, I think the response is already in your question.
You can directly write your tests in the test class.
public class MyDatabaseTests : IClassFixture<DatabaseFixture>
{
DatabaseFixture fixture;
public MyDatabaseTests( DatabaseFixture fixture )
{
this.fixture = fixture;
}
// Write test method here
[Fact]
private void MyTestMethod()
{
// Prepare Test
/// Prepare your test data here.
// Execute Test
/// Execute your test operation here.
// Validate Test
/// Use Assert methods here.
/// Assert.True(....);
}
}
Related
I have bunch of tests class such as CustomerTest, ProductTest, VendorTest and so on. I'm using in-memory Database and I would like to seed all data that I need to ONLY ONCE before running all these Tests above but have no idea how to do it.
I have CustomWebApplicationFactory class
public class CustomWebApplicationFactory<TProgram> : WebApplicationFactory<Program>
{
public ApplicationContext context { get; set; }
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureTestServices(services => {
var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<ApplicationContext>));
if (descriptor != null)
services.Remove(descriptor);
services.AddDbContext<ApplicationContext>(options => options
.UseInMemoryDatabase("testDB")
);
var serviceProvider = services.BuildServiceProvider();
using (var scope = serviceProvider.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var db = scopedServices.GetRequiredService<ApplicationContext>();
db.Database.EnsureDeleted();
db.Database.EnsureCreated();
SeedData(db); //seeding all data to all table
}
});
}
}
My Tests class
[TestClass]
public class VendorTest : BaseTest
{
[TestMethod]
public async Task AddVendor()
{
var content = JsonSerializer.Serialize(
new
{
name = "VENDORTEST"
}
);
var response = await _httpClient.PostAsync(
"vendors",
new StringContent(content, Encoding.UTF8, "application/json")
);
Assert.IsTrue(response.IsSuccessStatusCode);
}
[TestMethod]
public async Task GetVendor()
{
var response = await _httpClient.GetAsync("vendors");
string result = await response.Content.ReadAsStringAsync();
Assert.IsNotNull(response);
}
}
Base Test
public class BaseTest
{
protected static CustomWebApplicationFactory<Program> _webAppFactory = new CustomWebApplicationFactory<Program>();
protected static HttpClient _httpClient = _webAppFactory.CreateDefaultClient();
[AssemblyInitialize]
public void Initialize()
{
//not sure if BaseClass is the way to do it
//_httpClient = _webAppFactory.CreateDefaultClient();
}
}
Everytime a TestMethod is running, it will reseed all of the data due to CustomWebApplicationFactory. Any idea how to do it only once?
First of all: I dislike your idea so much, that I hesitated to show you a possible approach. I'll explain that after the code.
If you need something, that runs only once, static things come to mind. So, you could use a static/singleton factory to create your in-memory database like this:
public class TestDbFactory
{
private static TestDbFactory instance;
private readonly ApplicationContext applicationContext;
private TestDbFactory()
{
var databaseName = Guid.NewGuid().ToString();
var inMemoryContextOptions = new DbContextOptionsBuilder<ApplicationContext>()
.UseInMemoryDatabase(databaseName)
.Options;
this.applicationContext = new ApplicationContext(inMemoryContextOptions);
this.InitDatabase();
}
public static TestDbFactory Instance => CreateOrReuseInstance();
public ApplicationContext ApplicationContext => this.applicationContext;
private static TestDbFactory CreateOrReuseInstance()
{
if (instance != null) return instance;
var semaphore = new SemaphoreSlim(1, 1);
instance = instance ?? new TestDbFactory();
semaphore.Release(1);
return instance;
}
private void InitDatabase()
{
// ensure deleted & created & seeded for this.applicationContext
}
}
Now you can either use TestDbFactory.Instance.ApplicationContext to pass the database to the systems under test or you can use the factory pattern of the dependency injection setup to provide the context:
services.AddScoped(_ => TestDbFactory.Instance.ApplicationContext);
What I don't like about your idea:
Even for a single test you have to seed the whole database.
You can't do write test, because then tests don't have a predictable setup (it might be or not, that the db contents have changed before a test runs).
To me a test also serves as documentation. This is not possible, if the database content relevant for a specific test can't be determined from the test.
Adding new tests and updating the seeding will become a nightmare as you (let alone a whole team) have to verify the new data vs. all existing tests.
You won't save much typing as you still have to data for every test.
So, to me a better approach would be to use an in-memory database in every test class (a test class contains all tests for a single method). It would be ok to provide a basic seeding common to all tests in the test class, but specific data setup for a test should go with the test.
I hope that this post helps in one way or the other.
I try to make some integration testing for repository pattern with the database, I face a problem that each test run individually fine but when it tries to run all tests xUnit throw the following error
Message:
System.AggregateException : One or more errors occurred. (User does not have permission to alter database 'PharoesTechDBTest', the database does not exist, or the database is not in a state that allows access checks.
ALTER DATABASE statement failed.) (The following constructor parameters did not have matching fixture data: DatabaseFixture fixture)
---- Microsoft.Data.SqlClient.SqlException : User does not have permission to alter database 'PharoesTechDBTest', the database does not exist, or the database is not in a state that allows access checks.
ALTER DATABASE statement failed.
---- The following constructor parameters did not have matching fixture data: DatabaseFixture fixture
DatabaseFixture
public class DatabaseFixture : IDisposable
{
public DatabaseFixture()
{
var factory = new ApplicationContextFactory();
Context = factory.CreateDbContext(Array.Empty<string>());
// drop and recreate new database
Context.Database.EnsureDeleted();
Context.Database.EnsureCreated();
}
public void Dispose()
{
Context.Dispose();
}
public ApplicationDbContext Context { get; }
}
first Test class
public class CategorysRepoTests : IClassFixture<DatabaseFixture>, IClassFixture<LoggerFixture>
{
private readonly ICategoryRepo _repo;
private readonly DatabaseFixture _dbfixture;
public CategorysRepoTests(DatabaseFixture fixture, LoggerFixture loggerFixture)
{
_dbfixture = fixture;
_repo = new CategoriesRepo(fixture.Context, loggerFixture.Logger);
}
[Fact]
[Trait("Category", "IntegrationTest")]
public async Task CreateCategorySuccessfully()
{
Assert.Empty(_dbfixture.Context.Categories);
var newCategory = DataGenerator.CreateCategory();
// insert category
await _repo.AddAsycn(newCategory);
_dbfixture.Context.ChangeTracker.Clear();
// Check if ID was set
Assert.True(newCategory.Id > 0);
// Make sure that Category is in DB
var categories = await _dbfixture.Context.Categories.Where(u => u.Id == newCategory.Id).ToListAsync();
Assert.NotEmpty(categories);
}
}
second test class
public class MediaTypeRepoTests : IClassFixture<DatabaseFixture>,IClassFixture<LoggerFixture>
{
private readonly DatabaseFixture _dbfixture;
private readonly IMediaTypeRepo _repo;
public MediaTypeRepoTests(DatabaseFixture fixture,LoggerFixture logger)
{
_dbfixture = fixture;
_repo = new MediaTypesRepo(_dbfixture.Context, logger.Logger);
}
[Fact]
[Trait("Category", "IntegrationTest")]
public async Task MediaTypeRepoContainsDatatOnNewlyCreatedDb()
{
var medias = await _repo.GetAllAsync();
Assert.NotEmpty(medias);
}
}
DataGenerator
public class DataGenerator
{
public static Category CreateCategory()
{
return new Category
{
Title = "test title",
ThumbnailImagePath = "some thubnail",
};
}
}
Why each one of them run individually but when I try to run together it throws error ??! Where is the flaw?
As mentioned by #Christopher it was a parallelism issue, I solve it by disable the parallelism on the whole project level
global using Xunit;
[assembly: CollectionBehavior(DisableTestParallelization = true)]
read more about Tests in Parallel
I have written unit test cases where I have my test cases written against Cosmos Db emulator. (Those who don't know what emulator is , it is a local development cosmos Db provided by Microsoft which are generally used to test your queries)
In my unit test case I am instantiating the Emulator db and then running the test cases. problem occurs when I push this changes to my Azure devops pipeline. there the test cases fails with error as
Target machine actively refused the connection.
It does mean it is not able to instansiate db. How can i fix this. Any idea??
here is the initial code for testing
public class CosmosDataFixture : IDisposable
{
public static readonly string CosmosEndpoint = "https://localhost:8081";
public static readonly string EmulatorKey = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
public static readonly string DatabaseId = "testdb";
public static readonly string RecordingCollection = "testcolec";
public static string Root = Directory.GetParent( Directory.GetCurrentDirectory() ).Parent.Parent.FullName;
public static DocumentClient client { get; set; }
public async Task ReadConfigAsync()
{
// StartEmulatorDatabaseFromPowerShell();
client = new DocumentClient( new Uri( CosmosEndpoint ), EmulatorKey,
new ConnectionPolicy
{
ConnectionMode = ConnectionMode.Direct,
ConnectionProtocol = Protocol.Tcp
} );
await client.CreateDatabaseIfNotExistsAsync( new Database { Id = DatabaseId } );
await client.CreateDocumentCollectionIfNotExistsAsync( UriFactory.CreateDatabaseUri( DatabaseId ),
new DocumentCollection { Id = RecordingCollection } );
await ReadAllData( client );
}
public CosmosDataFixture()
{
ReadConfigAsync();
}
public void Dispose()
{
DeleteDatabaseFromPowerShell();// this is also defined in above class
}
}
public class CosmosDataTests : IClassFixture<CosmosDataFixture>
{ // mu unit test case goes here
You need to add this statement to your yaml Pipeline:
- task: PowerShell#2
inputs:
targetType: 'inline'
script: |
Import-Module "$env:ProgramFiles\Azure Cosmos DB Emulator\PSModules\Microsoft.Azure.CosmosDB.Emulator"
Start-CosmosDbEmulator
And the Connection String for CosmosDB should be: AccountEndpoint=https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==
You can instantiate the CosmosDB client like that:
var connectionString = "AccountEndpoint=https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
var client = new CosmosClient(connectionString);
var database = client.CreateDatabaseIfNotExistsAsync("testdb");
var container = await database.Database.CreateContainerIfNotExistsAsync("testcolec", "/partitionKey");
I'm trying to create a unit test that will test my MassTransit (RabbitMq) consumer. My consumer is expected a bus header key called UserName. I've simplified my consumer class as follows. I need to find a way to mock the bus header data otherwise the test will always raise an exception when it executes context.Headers.TryGetHeader("UserName", out object value)
I'm using the NSubstitute mocking library. So how do I mock the ConsumeContext<MyDataObject> type, and set a mock header value? I prefer a solution with NSubstitute
public class MyDataObjectCommand
{
public string MyProperty { get; set; }
}
// Consumer class
public class MyConsumer : IConsumer<MyDataObjectCommand>
{
private readonly IHubContext<MessageHub> _hubContext;
public MyConsumer(IHubContext<MessageHub> hubContext)
{
_hubContext = hubContext;
}
public async Task Consume(ConsumeContext<MyDataObjectCommand> context)
{
var myProperty = context.Message.MyProperty;
var userName = TryGetHeader(context, "UserName");
DoSomethingWith(_hubContext, myProperty, userName);
}
private string TryGetHeader(ConsumeContext<MyDataObjectCommand> context, string key, bool errorOnNull = true)
{
if (context.Headers.TryGetHeader(key, out object value))
{
return Convert.ToString(value);
}
if (errorOnNull)
{
throw new MyConsumerException(_reportName, $"{key} is not found", _hubContext);
}
return null;
}
}
// Unit Test (I'm using XUnit)
public class MyConsumerTests
{
private readonly IHubContext<MessageHub> _hubContext;
public MyConsumerTests()
{
_hubContext = Substitute.For<IHubContext<MessageHub>>();
}
[Fact]
public async Task ShouldConsume()
{
// arrange
var mockDataObject = new MyDataObjectCommand() { MyProperty = "x" };
var harness = new InMemoryTestHarness();
harness.Consumer(() => new MyConsumer(_hubContext));
// I need to mock header values on ConsumeContext<MyDataObjectCommand> object here
// but how do I do it using NSubstitute?
// context.Headers["UserName"] = "Bob"; ??
await harness.Start();
try
{
// act
await harness.InputQueueSendEndpoint.Send(mockDataObject);
// assert
Assert.True(await harness.Consumed.Any<MyDataObjectCommand>());
Assert.False(await harness.Consumed.Any<Fault<MyDataObjectCommand>>());
//Assert.Equal(context.Header["UserName"], "Bob"); How do I do the assertion??
}
finally
{
await harness.Stop();
}
}
}
You don’t mock header values when using MassTransit, you just have to set them when you’re sending the message using the test harness. There is absolutely zero need for a mocking framework.
await harness.InputQueueSendEndpoint.Send(mockDataObject,
context => context.Headers.Set(“UserName”, “Bob”));
The header will then be available on the ConsumeContext when the message is delivered to the consumer.
I got an application with two types of users. Say, we got user
A (password: 1234)
B (password: ABCD)
This is an example for a test:
[TestFixture]
public class TestCalls
{
private static RestApiClient client;
[SetUp]
public void Init()
{
client = new RestApiClient("http://localhost:1234/");
SetToken("A", "1234");
}
[Test]
public async Task ExampleTest()
{
// a test methods
var value = await client.ExecuteRequestAsync(...);
Assert.That(value, Is.Not.Null.And.Not.Empty)
// more assertions
}
}
SetToken simply sets the authentication-token on my RestApiClient-insance.
The problem is that user A gets other values than user B (same type of course, different values, but another database)
I could solve it with using TestCaseAttribute but I want to have the SetToken in SetUpAttribute-method Init()
[Test]
[TestCase("A")]
[TestCase("B")]
public async Task ExampleTest(string user)
{
SetToken(user, "1234"); // of course setting right password
// a test methods
var value = await client.ExecuteRequestAsync(...);
Assert.That(value, Is.Not.Null.And.Not.Empty)
// more assertions
}
Is there any possibility to have s.th like configurations for NUnit? So I could run everything twice (for both users)?
Or what could I do to test both users?
(Copy-pasting all tests is not a solution)
Found the solution:
We can add multiple TestFixture-attributes and give them values.
We need to define a constructor for the test-class with the same amount of arguments.
Then in the constructor we assign those values to fields (here I'm using private readonly fields)
And then we can use them in the SetUp.
NUnit automatically creates now test-cases for both users.
my Test-class looks like this now:
[TestFixture("A", "1234")]
[TestFixture("B", "ABCD")]
public class TestCalls
{
private static RestApiClient client;
private readonly string username;
private readonly string password;
public TestCalls(string username, string password)
{
this.username = username;
this.password = password;
}
[SetUp]
public void Init()
{
client = new RestApiClient("http://localhost:1234/");
SetToken(this.username, this.password);
}
[Test]
public async Task ExampleTest()
{
// a test methods
var value = await client.ExecuteRequestAsync(...);
Assert.That(value, Is.Not.Null.And.Not.Empty)
// more assertions
}
}