I am a newbie to XUnit and Moq. I have a method which takes string as an argument.How to handle an exception using XUnit.
[Fact]
public void ProfileRepository_GetSettingsForUserIDWithInvalidArguments_ThrowsArgumentException() {
//arrange
ProfileRepository profiles = new ProfileRepository();
//act
var result = profiles.GetSettingsForUserID("");
//assert
//The below statement is not working as expected.
Assert.Throws<ArgumentException>(() => profiles.GetSettingsForUserID(""));
}
Method under test
public IEnumerable<Setting> GetSettingsForUserID(string userid)
{
if (string.IsNullOrWhiteSpace(userid)) throw new ArgumentException("User Id Cannot be null");
var s = profiles.Where(e => e.UserID == userid).SelectMany(e => e.Settings);
return s;
}
The Assert.Throws expression will catch the exception and assert the type. You are however calling the method under test outside of the assert expression and thus failing the test case.
[Fact]
public void ProfileRepository_GetSettingsForUserIDWithInvalidArguments_ThrowsArgumentException()
{
//arrange
ProfileRepository profiles = new ProfileRepository();
// act & assert
Assert.Throws<ArgumentException>(() => profiles.GetSettingsForUserID(""));
}
If bent on following AAA you can extract the action into its own variable.
[Fact]
public void ProfileRepository_GetSettingsForUserIDWithInvalidArguments_ThrowsArgumentException()
{
//arrange
ProfileRepository profiles = new ProfileRepository();
//act
Action act = () => profiles.GetSettingsForUserID("");
//assert
ArgumentException exception = Assert.Throws<ArgumentException>(act);
//The thrown exception can be used for even more detailed assertions.
Assert.Equal("expected error message here", exception.Message);
}
Note how the exception can also be used for more detailed assertions
If testing asynchronously, Assert.ThrowsAsync follows similarly to the previously given example, except that the assertion should be awaited,
public async Task Some_Async_Test() {
//...
//Act
Func<Task> act = () => subject.SomeMethodAsync();
//Assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(act);
//...
}
If you do want to be rigid about AAA then you can use Record.Exception from xUnit to capture the Exception in your Act stage.
You can then make assertions based on the captured exception in the Assert stage.
An example of this can be seen in xUnits tests.
[Fact]
public void Exception()
{
Action testCode = () => { throw new InvalidOperationException(); };
var ex = Record.Exception(testCode);
Assert.NotNull(ex);
Assert.IsType<InvalidOperationException>(ex);
}
It's up to you what path you want to follow, and both paths are fully supported by what xUnit provides.
You could consider something like this if you want to stick to AAA:
// Act
Task act() => handler.Handle(request);
// Assert
await Assert.ThrowsAsync<MyExpectedException>(act);
I think there are two way to handle this scenario which I personally like. Suppose I have below method which I want to test
public class SampleCode
{
public void GetSettingsForUserID(string userid)
{
if (string.IsNullOrWhiteSpace(userid)) throw new ArgumentException("User Id
Cannot be null");
// Some code
}
}
I can test this with below testcase,Make sure you add FluentAssertions nuget in your test project.
public class SampleTest
{
private SampleCode _sut;
public SampleTest()
{
_sut = new SampleCode();
}
[Theory]
[InlineData(null)]
[InlineData(" ")]
public void TestIfValueIsNullorwhiteSpace(string userId)
{
//Act
Action act= ()=> _sut.GetSettingsForUserID(userId);
// Assert
act.Should().ThrowExactly<ArgumentException>().WithMessage("User Id Cannot be null");
}
}
but I found one problem here, whitespace and Null are two different things.
c# provides ArgumentException for whitespace and ArgumentNullException for null reference.
So You can refactor your code something like this
public void GetSettingsForUserID(string userid)
{
Guard.Against.NullOrWhiteSpace(userid, nameof(userid));
}
Here you need Ardalis.GuardClauses nuget in your code project And testcase will be something like this
[Fact]
public void TestIfValueIsNull()
{
//Act
Action act = () => _sut.GetSettingsForUserID(null);
//Assert
act.Should().ThrowExactly<ArgumentNullException>().WithMessage("*userId*");
}
[Fact]
public void TestIfValueIsWhiteSpace()
{
//Act
Action act= ()=> _sut.GetSettingsForUserID(" ");
//Assert
act.Should().ThrowExactly<ArgumentException>().WithMessage("*userId*");
}
Instead of following complicated protocols I found it most convenient to use a try catch block :
try
{
var output = Settings.GetResultFromIActionResult<int>(controller.CreateAllFromExternalAPI());
Assert.True(output > 0);
}
catch(InvalidOperationException e)
{
Assert.True("Country table can only be filled from ExternalAPI if table is blank"==e.Message);
}
Related
I have a service for adding city in DB. It works good, now I need test it. I have never worked with tests before. I'll be grateful for your recommendations. I would test case - If it isn't null - test passed(I mean if User insert anything - test is passed, if he doesn't insert data - test failed). I need just check townName null or no. It is my TestClass
[TestClass]
public partial class TownRepositoryTests
{
[TestMethod]
public async Task InsertTownName_ReturnTownName()
{
var builder = new RepositoryBuilder<TownRepository>().SetupManager();
using (builder.CreateIsolationScope())
{
var expectedTown = await TestBuilder.SetupTownNameAsync(builder);
var repository = builder.Build();
var townName = expectedTown.Name;
var result = await repository.InsertTown(townName);
Assert.IsNotNull(result);
}
}
}
SetupTownNameAsync method(here I add data). I think it works good, because when I used debug test, I checked that this method return (id=1, name= "TestTown")
public static class TestBuilder
{
public static async Task<Town> SetupTownNameAsync(RepositoryBuilder<TownRepository> builder)
{
var town = await builder.CreateTownAsync(name: "TestTown");
var setupResult = new Town
{
Id = town.Id,
Name = town.Name
};
return setupResult;
}
}
And here my InsertTown method
public async Task<int> InsertTown(string townName)
{
var parameters = new { townName };
return await Connection.QueryFirstOrDefaultAsync<int>(Adhoc["AddTown"], parameters, Transaction);
}
When I checked problem with debug test - in this line returns "TestTown", but when I check it in Assert - my test is failed
var result = await repository.InsertTown(townName);
Assert.IsNotNull(result);
I developed Unit test for my service. Now my test check on inserting Name and 2nd check on a null.
[TestMethod]
public async Task InsertTownName_ReturnTownName()
{
var builder = new RepositoryBuilder<TownRepository>().SetupManager();
using (builder.CreateIsolationScope())
{
var repository = builder.Build();
var townName = "Town" + DateTime.UtcNow.ToString();
await repository.InsertTown(townName);
var connection = builder.TransactionManager.Connection;
var transaction = builder.TransactionManager.Transaction;
var result = await connection.ReadSingleOrDefaultAsync<Town>(x => x.Name == townName, transaction: transaction);
Assert.IsNotNull(result);
Assert.AreEqual(townName, result.Name);
}
}
[TestMethod]
public async Task InsertNullName()
{
var builder = new RepositoryBuilder<TownRepository>().SetupManager();
using (builder.CreateIsolationScope())
{
var repository = builder.Build();
await repository.InsertTown(null);
var connection = builder.TransactionManager.Connection;
var transaction = builder.TransactionManager.Transaction;
var result = await connection.ReadSingleOrDefaultAsync<Town>(x => x.Name == null, transaction: transaction);
Assert.IsNull(result.Name);
Assert.AreEqual(null, result.Name);
}
}
Both of this method works good. Next step I need check on Empty (If in line where user need insert town name - empty name). I have no idea how implement it. Could You recommend to me and could you check 2nd method for working with null. Is it correct unit test? Here my method that I tested
public async Task<int> InsertTown(string townName)
if (String.IsNullOrEmpty(townName))
{
throw new Exception();
}
else
{
var parameters = new { townName };
return await Connection.QueryFirstOrDefaultAsync<int>(Adhoc["AddTown"], parameters, Transaction);
}
Here is an example of what a method with input parameters checking might look like.
public async Task<int> InsertTown(string townName)
{
if (townName == null)
{
throw new ArgumentNullException(nameof(townName));
}
if (string.IsNullOrWhiteSpace(townName))
{
throw new ArgumentException("Parameter cannot be empty", nameof(townName));
}
var parameters = new { townName };
//return await Connection.QueryFirstOrDefaultAsync<int>(Adhoc["AddTown"], parameters, Transaction);
return await Task.Run(() => 42); // for testing puprposes
}
You can test it like this
[TestMethod]
public async Task InsertTown_NullString_ThrowsArgumentNullExceptionAsync()
{
string townName = null;
// var repository = ...;
await Assert.ThrowsExceptionAsync<ArgumentNullException>(() =>
repository.InsertTown(townName));
}
[DataTestMethod]
[DataRow("")]
[DataRow(" ")]
public async Task InsertTown_EmptyString_ThrowsArgumentExceptionAsync(string value)
{
string townName = value;
// var repository = ...;
await Assert.ThrowsExceptionAsync<ArgumentException>(() =>
repository.InsertTown(townName));
}
As mentioned in the comments, it looks like your unit tests are more like integration tests.
In unit tests, you should not access neither real database, work with a network, nor work with a file system.
You have to mock any dependencies. For example with NSubstitute, FakeItEasy or Moq.
Your Connection.QueryFirstOrDefaultAsync method is static, right?
It is highly recommendable to rewrite the code, getting rid of static methods. With the extraction of abstractions to interfaces. Because static methods, like in your case, are untested or difficult to test.
I'm trying to learn unit testing in xUnit for ASP.NET Core projects. In order to achieve it, I have created simple ASP.NET Core project to perform tests on it. However I cannot get 100% cover of tests due to wrong testing an exception that's being caught inside controller method.
Here is my controller method I'm testing:
[HttpGet]
public async Task<IEnumerable<User>> GetUsers()
{
try
{
var users = await _repository.User.GetAllUsersAsync();
return users;
}
catch (Exception e)
{
_logger.LogError($"Error in GetUsers: {e}");
return null;
}
}
And here is my unit test method in xUnit:
[Fact]
public async Task GetUsers_WhenCalled_ReturnsCorrectAmountOfUsers()
{
//Arrange
var mockRepo = new Mock<IRepositoryWrapper>();
mockRepo.Setup(repo => repo.User.GetAllUsersAsync())
.ReturnsAsync(GetTestUsers());
var controller = new UsersController(mockRepo.Object, _logger, _service);
//Act
var result = await controller.GetUsers();
//Assert
var model = Assert.IsAssignableFrom<IEnumerable<User>>(result);
model.Count().Should().Be(3);
Assert.Throws<NullReferenceException>(() =>
_controller.GetUsers().Exception);
}
When I run tests, everything gets green status, however inside the controller class I cannot see a 'green tick' next to the lines with catch block scope. I'd really like to know how to write proper code for testing an exceptions inside catch blocks!
Another test is needed that will cause the exception to be thrown when being exercised.
For example
[Fact]
public async Task GetUsers_WhenCalled_HandlesException() {
//Arrange
var mockRepo = new Mock<IRepositoryWrapper>();
mockRepo
.Setup(repo => repo.User.GetAllUsersAsync())
.ThrowsAsync(new InvalidOperationException());
var controller = new UsersController(mockRepo.Object, _logger, _service);
//Act
var result = await controller.GetUsers();
//Assert
Assert.IsNull(result);
//Can also assert what logger records
}
In the above example, when GetAllUsersAsync is invoked, it will throw an exception that will be caught in the try-catch and allow the code to flow as intended for the test.
Method Under Test
protected override async Task<Name> DoExecuteAsync(NameContext context)
{
context.ThrowIfNull("context");
var request = new Request
{
Id = context.Id,
Principal = context.UserPrincipal,
};
return await this.repository.NameAsync(request, new CancellationToken(), context.ControllerContext.CreateLoggingContext());
}
protected override Name HandleError(NameContext viewContext, Exception exception)
{
if (this.errorSignaller != null)
{
this.errorSignaller.SignalFromCurrentContext(exception);
}
return Name.Unknown;
}
This is implementation of
public abstract class BaseQueryAsync<TInput, TOutput> : IQueryAsync<TInput, TOutput>
{
public async Task<TOutput> ExecuteAsync(TInput context)
{
try
{
return await this.DoExecuteAsync(context);
}
catch (Exception e)
{
return this.HandleError(context, e);
}
}
protected abstract Task<TOutput> DoExecuteAsync(TInput context);
protected virtual TOutput HandleError(TInput viewContext, Exception exception)
{
ExceptionDispatchInfo.Capture(exception).Throw();
}
}
Test Case goes like below
[SetUp]
public void Setup()
{
var httpContext = MvcMockHelpers.MockHttpContext(isAuthenticated: true);
this.controller = new Mock<Controller>();
this.controller.Object.SetMockControllerContext(httpContext.Object);
this.repoMock = new Mock<IRepository>();
this.errorSignaller = new Mock<IErrorSignaller>();
this.query = new NameQuery(this.repoMock.Object, this.errorSignaller.Object);
this.userPrinciple = new Mock<IPrincipal>();
this.context = new NameContext(this.controller.Object.ControllerContext, this.userPrinciple.Object);
}
[Test]
public async Task TestDoExecuteAsyncWhenRepositoryFails()
{
// Arrange
this.repoMock.Setup(
x => x.NameAsync(
It.IsAny<Request>(),
It.IsAny<CancellationToken>(),
It.IsAny<ILoggingContext>())).Throws<Exception>();
// Act
Func<Task<Name>> act = async () => await this.query.ExecuteAsync(this.context);
// Assert
act.ShouldNotThrow();
this.errorSignaller.Verify(s => s.SignalFromCurrentContext(It.IsAny<Exception>()), Times.Once);
}
To verify the Name Object ,When I use the var result = await act() before the line
this.errorSignaller.Verify(s => s.SignalFromCurrentContext(It.IsAny<Exception>()), Times.Once);
The this.errorSignaller.Verify fails since it's count is 2 instead of 1. My intention is to check the Name object along with below code.
act.ShouldNotThrow();
this.errorSignaller.Verify(s => s.SignalFromCurrentContext(It.IsAny<Exception>()), Times.Once);
I knew that if I write a new test case I can easily verify it, but is there any way I can do altogether in this test?
If you want to test the result then use:
Name result = await this.query.ExecuteAsync(this.context);
result.Should().Be(expectefResult);
Make sure to make your test method public async Task
Update
To be able to verify name you would need to set it in the function.
//...code removed for brevity
Name expectedName = Name.Unknown;
Name actualName = null;
// Act
Func<Task> act = async () => {
actualName = await this.query.ExecuteAsync(this.context);
};
// Assert
act.ShouldNotThrow();
actualName
.Should().NotBeNull()
.And.Be(expectedName);
//...rest of code
Original
As already mentioned in the comments, act is a function that returns a Task.
While its implementation is awaited, the function itself still needs to be invoked. And since the function returns a Task it too would need to be awaited.
Func<Task<Name>> act = async () => await this.query.ExecuteAsync(this.context);
var name = await act();
It is the same as having the following function.
async Task<Name> act() {
return await this.query.ExecuteAsync(this.context);
}
You would have to await it the same way
var name = await act();
The only difference being that the former example has the function in a delegate.
Try to avoid mixing blocking calls like .Result with async/await code. This tends to cause deadlocks.
You can try to check it with
await query.ExecuteAsync(this.context);
or
this.query.ExecuteAsync(this.context).GetAwaiter().GetResult();
and in case of Func:
act.Invoke().GetAwaiter().GetResult();
I'm relatively new to TDD and I have a project with repository pattern with EF in the repository class.
Now I want to test these repositories and I have already mocked the DbContext successfully. (Using Moq and nunit)
Now I want to validate adding a object to my mocked database. The test says, test passed, but I'm quite sure, my test is not correct because I queried the database before the Add method, returns 5 objects, then I call the Add method and query the database again, still only 5 objects, but I would expect now 6 objects. Am I missing here something?
Model Address.cs
public class Address
{
public int Id { get; set; }
public string Person {get; set; }
//some more properties
}
Repository.cs
public async Task<AdresseDto> AddAdresseAsync(AdresseDto adresseDto)
{
try
{
ctx.Adresse.Add(adresse);
await ctx.SaveChangesAsync();
adresseDto.Id = adresse.Id;
}
catch (Exception e)
{
Console.WriteLine("ohoh");
Console.WriteLine(e.ToString());
Console.WriteLine("ohoh");
return null;
}
return adresseDto;
}
UnitTest.cs
public async Task Test_AddTestAddress_ReturnsAddressWithId()
{
var dummyDataInDb = AddressHelper.GetAdressen(5);
var dummyData4Db = AddressHelper.GetAdressen(1).FirstOrDefault();
//AddressHelper returns just some dummy objects
InitializeMock<Adresse>.With(dummyDataInDb.AsQueryable(), out AdresseMock);
XaDbMock = new Mock<xaModel>();
XaDbMock
.Setup(x => x.Adresse)
.Returns(AdresseMock.Object);
_repository = NewAddressRepository(XaDbMock.Object);
//When
var addressesInDb = await _repository.GetAllAdressenAsync();
var result = await _repository.AddAdresseAsync(dummyData4Db);
var addressesInDbAfter = await _repository.GetAllAdressenAsync();
//Then
Assert.IsNotNull(addressesInDb);
Assert.AreEqual(5, addressesInDb.Count);
Assert.IsNotNull(result);
Assert.AreEqual(0, result.Id);
Assert.IsNotNull(addressesInDbAfter);
Assert.AreEqual(6, addressesInDbAfter.Count);
}
InitializeMock.cs
public class InitializeMock<T> where T : class
{
public static void With(IQueryable<T> dummyData, out Mock<DbSet<T>> mock)
{
mock = new Mock<DbSet<T>>();
//Required for async tasks
mock.As<IDbAsyncEnumerable>()
.Setup(x => x.GetAsyncEnumerator())
.Returns(new TestDbAsyncEnumerator<T>(dummyData.GetEnumerator()));
//Required for async tasks
mock.As<IQueryable<T>>()
.Setup(x => x.Provider)
.Returns(new TestDbAsyncQueryProvider<T>(dummyData.Provider));
mock.As<IQueryable<T>>()
.Setup(x => x.Expression)
.Returns(dummyData.Expression);
mock.As<IQueryable<T>>()
.Setup(x => x.ElementType)
.Returns(dummyData.ElementType);
mock.As<IQueryable<T>>()
.Setup(x => x.GetEnumerator())
.Returns(dummyData.GetEnumerator);
}
}
If something is missing, please let me know. I'll provide any neccessary informations you need.
Thanks in advance