Testing with multiple settings in NUnit - c#

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
}
}

Related

How to seed data only once before running all tests using MSTest

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.

xunit throw AggregateException(User does not have permission to alter database) while each test is run individually

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

How to mock / unit test HTTP Client - restease

tl;dr: I'm having trouble mocking restease**
Also, I realize I may be totally on the wrong track, so any suggestions / nudges in the right direction would be of great help. I am quite new to this.
I'm making a small HTTP Client library, built around RestEase. RestEase is nice and easy to use, but I'm having trouble mocking the calls for the purpose of unit testing.
I want to use moq and NUnit, but I can't properly mock the RestClient. Example (shortened for brevity):
IBrandFolderApi - interface needed by restease to send calls
public interface IBrandFolderApi
{
[Post("services/apilogin")]
Task<LoginResponse> Login([Query] string username, [Query] string password);
}
BrandfolderClient.cs - the main class
public class BrandfolderClient : IBrandfolderClient
{
private IBrandFolderApi _brandFolderApi { get; set; }
public BrandfolderClient(string url)
{
_brandFolderApi = RestClient.For<IBrandFolderApi >(url);
}
public async Task<string> Login(string username, string password)
{
LoginResponse loginResponse = await _brandFolderApi .Login(username, password);
if (loginResponse.LoginSuccess)
{
....
}
....
return loginResponse.LoginSuccess.ToString();
}
}
The unit tests
public class BrandFolderTests
{
BrandfolderClient _brandfolderClient
Mock<IBrandFolderApi> _mockBrandFolderApii;
[SetUp]
public void Setup()
{
//The test will fail here, as I'm passing a real URL and it will try and contact it.
//If I try and send any string, I receive an Invalid URL Format exception.
string url = "https://brandfolder.companyname.io";
_brandfolderClient = new BrandfolderClient (url);
_mockBrandFolderApii= new Mock<IBrandFolderApi>();
}
....
}
So, I don't know how to properly mock the Restclient so it doesn't send an actual request to an actual URL.
The test is failing at the constructor - if I send a valid URL string, then it will send a call to the actual URL. If I send any other string, I get an invalid URL format exception.
I believe I haven't properly implemented something around the rest client, but I'm not sure where. I'm very stuck on this, I've been googling and reading like crazy, but I'm missing something and I don't know what.
So, I don't know how to properly mock the Restclient so it doesn't send an actual request to an actual URL.
You actually should not have any need to mock RestClient.
Refactor your code to depend explicitly on the abstraction you control
public class BrandfolderClient : IBrandfolderClient {
private readonly IBrandFolderApi brandFolderApi;
public BrandfolderClient(IBrandFolderApi brandFolderApi) {
this.brandFolderApi = brandFolderApi; //RestClient.For<IBrandFolderApi >(url);
}
public async Task<string> Login(string username, string password) {
LoginResponse loginResponse = await brandFolderApi.Login(username, password);
if (loginResponse.LoginSuccess) {
//....
}
//....
return loginResponse.LoginSuccess.ToString();
}
}
removing the tight coupling to static 3rd party implementation concerns will allow your subject to be more explicit about what it actually needs to perform its function.
This will also make it easier for the subject to be tested in isolation.
For example:
public class BrandFolderTests {
BrandfolderClient subject;
Mock<IBrandFolderApi> mockBrandFolderApi;
[SetUp]
public void Setup() {
mockBrandFolderApi = new Mock<IBrandFolderApi>();
subject = new BrandfolderClient(mockBrandFolderApi.Object);
}
//....
[Test]
public async Task LoginTest() {
//Arrange
LoginResponse loginResponse = new LoginResponse() {
//...
};
mockBrandFolderApi
.Setup(x => x.Login(It.IsAny<string>(), It.IsAny<string>()))
.ReturnsAsync(loginResponse);
//Act
string response = await subject.Login("username", "password");
//Assert
mockBrandFolderApi.Verify(x => x.Login(It.IsAny<string>(), It.IsAny<string>()), Times.Once);
}
}
In production code, register and configure the IBrandFolderApi abstraction with the container, applying what ever 3rd party dependencies are required
Startup.ConfigureServices
//...
ApiOptions apiOptions = Configuration.GetSection("ApiSettings").Get<ApiOptions>();
services.AddSingleton(apiOptions);
services.AddScoped<IBrandFolderApi>(sp => {
ApiOptions options = sp.GetService<ApiOptions>();
string url = options.Url;
return RestClient.For<IBrandFolderApi>(url);
});
Where ApiOptions is used to store settings
public class ApiOptions {
public string Url {get; set;}
//... any other API specific settings
}
that can be defined in appsetting.json
{
....
"ApiSettings": {
"Url": "https://brandfolder.companyname.io"
}
}
so that they are not hard coded all over you code.
The HttpClient comes from System.Net.Http, which is not easy to mock.
You can, however, create a test HttpClient by passing a fake HttpMessageHandler. Here is an example:
public class FakeHttpMessageHandler : HttpMessageHandler
{
private readonly bool _isSuccessResponse;
public FakeHttpMessageHandler(bool isSuccessResponse = true)
{
_isSuccessResponse = isSuccessResponse;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return Task.FromResult(
new HttpResponseMessage(_isSuccessResponse ? HttpStatusCode.OK : HttpStatusCode.InternalServerError));
}
}
You can create create a test instance of HttpClient as shown below:
var httpClient = new HttpClient(new FakeHttpMessageHandler(true))
{ BaseAddress = new Uri("baseUrl") };
Not sure how you are using verify on _httpClient, its not a mock. but what you are looking for is https://github.com/canton7/RestEase#custom-httpclient. Most people pass in factory for this
//constructor
public httpClientConstructor(string url, IHttpHandlerFactory httpHandler)
{
var httpClient = new HttpClient(httpHandler.GetHandler())
{
BaseAddress = new Uri(url),
};
_exampleApi = RestClient.For<IExampleApi>(url);
}
public interface IHttpHandlerFactory<T>
{
T GetHandler() where T: HttpMessageHandler
}
Thanks Ankit Vijay https://stackoverflow.com/a/68240316/5963888
public class FakeHttpMessageHandler : HttpMessageHandler
{
private readonly bool _isSuccessResponse;
public FakeHttpMessageHandler(bool isSuccessResponse = true)
{
_isSuccessResponse = isSuccessResponse;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return Task.FromResult(
new HttpResponseMessage(_isSuccessResponse ? HttpStatusCode.OK : HttpStatusCode.InternalServerError));
}
}
[SetUp]
public void Setup()
{
var fakeHandler = new Mock<IHttpHandlerFactory>();
fakeHandler.Setup(e => e.GetHandler() ).Returns( new FakeHttpHandler() );
_httpClient = new HttpClient(fakeHandler.Object);
_exampleApi = new Mock<IExampleApi>();
}

Writing Xunit test cases in C#

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(....);
}
}

Moq: setup a generic method with mocked parameters

I've been trying to write a few tests in NUnit for my generic methods, without success. I hope I can make my situation clear, since I had to heavily paraphrase my code.
DoBusinessLogic() is the method I want to test. It calls two other methods from the base class:
public class MySvc : BaseSvc, IMySvc
{
private readonly IMyRepo _repo;
private readonly IMyConnectorClass _connector
public MySvc(IMyRepo repo) : base(repo)
{
_repo = repo;
_connector = _repo.GetConnector();
}
public async Task DoBusinessLogic(int id1, int id2){
bool doesFirstObjectExist = await CheckMainObjExists<Foo>(id1, _connector);
await CheckSubObjExists<Bar>(id2, _connector);
// further business logic
}
}
Those methods, in turn, call the same repository method, but have different logic after it:
public abstract class BaseSvc : IBaseSvc
{
private readonly IBaseRepo _repo
protected BaseSvc(IBaseRepo repo)
{
_repo = repo;
}
protected async Task<bool> CheckMainObjExists<T>(int? id, IMyConnectorClass connector)
{
return await _repo.GetObjectByIdAsync<T>(Id, connector) != null;
}
protected async Task CheckSubObjExists<T>(int? id, IMyConnectorClass connector)
{
if (await _repo.GetObjectByIdAsync<T>(Id, connector) == null)
{ throw new Exception("Object not found!"); }
}
}
Next, I want to write unit a test for DoBusinessLogic() in the MySvc class. Unfortunately, it seems I can't mock the responses from the repository.
[TestFixture]
public class MySvcTests
{
private MySvc _svc;
private Mock<IMyRepo> _repoMock;
private Mock<IMyConnectorClass> _connectorMock;
[SetUp]
public void SetUp()
{
_repoMock = new Mock<IMyRepo>() {};
_connectorMock = new Mock<IMyConnectorClass>();
_repo.SetUp(r => r.GetConnector()).Return(_connectorMock.Object);
_svc = new MySvc(_repoMock);
}
/*
My intent in this test, is to make CheckMainObjExists() pass,
but make CheckSubObjExist() fail.
*/
[Test]
public async Task DoBusinessLogic_If2ndObjectNotExist_ThrowException()
{
// This should return an object
_repoMock.Setup(r => r.GetObjectByIdAsync<Foo>(It.IsAny<int>(), _connectorMock.Object))
.ReturnsAsync(new Foo());
// This should return null
_repoMock.Setup(r => r.GetObjectByIdAsync<Bar>(It.IsAny<int>(), _connectorMock.Object))
.ReturnsAsync((Bar) null);
Assert.Throws<Exception>(await _svc.DoBusinessLogic());
}
}
However, when I run the test, both methods that I set up for my mock repo return null, whereas I expect a "true" from the first.
I do not know where the problem is situated, but I have my suspicions:
Is it possible to setup a method, using a mocked object as a parameter? In this case, is it possible to use _connectorMock.Object as a setup parameter?
Is it possible to setup the same generic method multiple times, but for a different type each time? It's first setup for Foo, then for Bar.
I just tested this code and it runs as expected. Now I had to make a lot of assumptions just to get the code to compile and run which would mean that my test of your code is flawed as I may have fixed something that was omitted in your example.
I made no changes to your test setup code, which worked.
[TestClass]
public class MySvcTests {
[TestMethod]
[ExpectedException(typeof(Exception))]
public async Task DoBusinessLogic_If2ndObjectNotExist_ThrowException() {
var _repoMock = new Mock<IMyRepo>() { };
var _connectorMock = new Mock<IMyConnectorClass>();
_repoMock.Setup(r => r.GetConnector()).Returns(_connectorMock.Object);
var _svc = new MySvc(_repoMock.Object);
// This should return an object
_repoMock.Setup(r => r.GetObjectByIdAsync<Foo>(It.IsAny<int>(), _connectorMock.Object))
.ReturnsAsync(new Foo());
// This should return null
_repoMock.Setup(r => r.GetObjectByIdAsync<Bar>(It.IsAny<int>(), _connectorMock.Object))
.ReturnsAsync((Bar)null);
await _svc.DoBusinessLogic(0, 0);
}
}

Categories

Resources