How do I assert the exception thrown in the constructor? - c#

In my service class, I have this constructor.
public IngestService()
{
_extractedFilePath = configuration["Path:Ingest"];
if (string.IsNullOrEmpty(_extractedFilePath))
{
_logger.LogError($"Invalid conguration. Check appsettings.");
throw new Exception("Invalid conguration. Check appsettings.");
}
}
and I have this test using XUnit
[Fact]
public async Task WhenInvalidConstructor_ThenShouldThrowETest() {
var _ingestService = new IngestService();
}
When I debug it, it can reach the constructor. But how do I capture the exception and assert the exception message "Invalid conguration. Check appsettings."?

Try this:
Exception actualException = Assert.Throws<Exception>(() => new IngestService());
Assert.Equal("Invalid conguration. Check appsettings.", actualException.Message);

Related

Unit test fails when use try/catch

I need to test that exactly Argument Exception is caugtht. Is it really possible to understand that exception in method is caugtht?
public JsonResult Create(TeamViewModel teamViewModel)
{
JsonResult result = null;
try
{
// here exception throws
var domainTeam = teamViewModel.ToDomain();
...
}
catch (ArgumentException ex)
{
this.ModelState.AddModelError(string.Empty, ex.Message);
result = this.Json(this.ModelState);
}
return result;
}
My Unit test for this method:
public void Create_InvalidTeamAchievements_ArgumentExceptionThrown()
{
Exception exception = null;
string invalidAchievements = CreateInvalidTeamAchievements();
// Arrange
var viewModel = new TeamMvcViewModelBuilder().WithAchievements(invalidAchievements).Build();
var sut = _kernel.Get<TeamsController>();
// Act
try
{
sut.Create(viewModel);
}
catch (ArgumentException ex)
{
exception = ex;
}
// Assert
VerifyExceptionThrown(exception, string.Format(Resources.ValidationTeamAchievements,
Constants.Team.MAX_ACHIEVEMENTS_LENGTH));
}
You are testing it in wrong way. Functionality should be tested to not throw exception as you already have caught exception inside Create Method. Rather you should Assert that JsonResult containing your ModelState should have error in it in case exception was raised in Create method.

NUnit3: Assert.Throws with async Task

I am trying to port a test to NUnit3 and am getting a System.ArgumentException : 'async void' methods are not supported, please use 'async Task' instead.
[Test]
public void InvalidUsername()
{
...
var exception = Assert.Throws<HttpResponseException>(async () => await client.LoginAsync("notarealuser#example.com", testpassword));
exception.HttpResponseMessage.StatusCode.ShouldEqual(HttpStatusCode.BadRequest); // according to http://tools.ietf.org/html/rfc6749#section-5.2
...
}
Assert.Throws appears to take a TestDelegate, defined as:
public delegate void TestDelegate();
hence the ArgumentException. What is the best way to port this code?
This was resolved by Nunit. You can now use Assert.ThrowsAsync<>()
https://github.com/nunit/nunit/issues/1190
Example:
Assert.ThrowsAsync<Exception>(() => YourAsyncMethod());
I would recommend the following code instead of Assert.ThrowsAsync, as this is more readable:
// Option A
[Test]
public void YourAsyncMethod_Throws_YourException_A()
{
// Act
AsyncTestDelegate act = () => YourAsyncMethod();
// Assert
Assert.That(act, Throws.TypeOf<YourException>());
}
// Option B (local function)
[Test]
public void YourAsyncMethod_Throws_YourException_B()
{
// Act
Task Act() => YourAsyncMethod();
// Assert
Assert.That(Act, Throws.TypeOf<YourException>());
}
I ended up writing a static function that mirrors what NUnit does. There was a whole conversation at https://github.com/nunit/nunit/issues/464 about this.
public static async Task<T> Throws<T>(Func<Task> code) where T : Exception
{
var actual = default(T);
try
{
await code();
Assert.Fail($"Expected exception of type: {typeof (T)}");
}
catch (T rex)
{
actual = rex;
}
catch (Exception ex)
{
Assert.Fail($"Expected exception of type: {typeof(T)} but was {ex.GetType()} instead");
}
return actual;
}
Then from my tests I can use it such as
var ex = await CustomAsserts.Throws<HttpResponseException>(async () => await client.DoThings());
Assert.IsTrue(ex.Response.StatusCode == HttpStatusCode.BadRequest);
To ensure the exception was thrown, it's better to not assert in the catch block if you so choose to use one. This way, you can be sure the correct exception type is thrown because otherwise you'll get a null reference or an uncaught different exception.
HttpResponseException expectedException = null;
try
{
await client.LoginAsync("notarealuser#example.com", testpassword));
}
catch (HttpResponseException ex)
{
expectedException = ex;
}
Assert.AreEqual(HttpStatusCode.NoContent, expectedException.Response.BadRequest);
You could try using something like this:
try
{
await client.LoginAsync("notarealuser#example.com", testpassword);
}
catch (Exception ex)
{
Assert.That(ex, Is.InstanceOf(typeof (HttpResponseException)));
}

How to mock SendGrid

I am trying to write a unit test for a method I wrote which sends out an email with SendGrid.
My method is something like this:
public async Task SendEmail(TemplatedMailMessage emailMessage)
{
if (String.IsNullOrWhiteSpace(emailMessage.Html) || String.IsNullOrWhiteSpace(emailMessage.From.ToString()) || !emailMessage.To.Any())
{
throw new Exception("Html, From or To is empty");
}
try
{
// Send the email
await this.TransportWeb.DeliverAsync(emailMessage.GetSendGridMessage());
}
catch (Exception ex)
{
//do stuff
}
//log success
}
the TransportWeb is a property which is set in my constructor through a parameter so I can create a mock object.
public EmailManager(Web transportWeb = null)
{
this.TransportWeb = transportWeb ?? SetupSendGrid();
}
In my test method I am trying to mock the TransportWeb (of type SendGrid.Web) property:
[TestMethod]
public async Task SendEmail_ValidEmailTemplateAndNoParameters_EmailIsSent()
{
//ARRANGE
var templatedMailmessage = new Mock<TemplatedMailMessage>();
var transportWeb = new Mock<Web>();
transportWeb.SetupAllProperties();
transportWeb.Setup(x => x.DeliverAsync(It.IsAny<ISendGrid>()));
var emailManager = new EmailManager(transportWeb.Object);
//ACT
await emailManager.Send(templatedMailmessage.Object);
//ASSERT
transportWeb.Verify(x => x.DeliverAsync(It.IsAny<ISendGrid>()), Times.Once());
}
However, I get the following error:
Invalid setup on a non-virtual (overridable in VB) member: x =>
x.DeliverAsync
Does anybody have an idea how I can fix this?
Okay I fixed it :)
You should not use the Web class but ITransport interface:
var transport = new Mock<ITransport>();
transport.SetupAllProperties();
transport.Setup(x => x.DeliverAsync(It.IsAny<SendGridMessage>())).ReturnsTask();
var em = new EmailManager(transport.Object);
I also used the extensions methods created by Simon V.

How to handle exceptions thrown by Tasks in xUnit .net's Assert.Throws<T>?

The following asynchronous xUnit.net test with a lambda marked with the async modifier fails by reporting that no exception was thrown:
[Theory, AutoWebData]
public async Task SearchWithNullQueryThrows(
SearchService sut,
CancellationToken dummyToken)
{
// Fixture setup
// Exercise system and verify outcome
Assert.Throws<ArgumentNullException>(async () =>
await sut.SearchAsync(null, dummyToken));
// Teardown
}
To make sure that an ArgumentNullException is actually thrown I explicitly used a try-catch block. It worked, however the resulting code is not clean (compared to the first test):
[Theory, AutoWebData]
public async Task SearchWithNullQueryThrows(
SearchService sut,
CancellationToken dummyToken)
{
// Fixture setup
var expected = typeof(ArgumentNullException);
Type actual = null;
// Exercise system
try
{
await sut.SearchAsync(null, dummyToken);
}
catch (ArgumentNullException e)
{
actual = e.GetType();
}
// Verify outcome
Assert.Equal(expected, actual);
// Teardown
}
Why the Assert.Throws<T> with the lambda marked with the async modifier fails?
Update
This has been solved in xUnit 2, with the addition of Assert.ThrowsAsync.
I am suspecting that Assert.Throws is not async-aware. I recommend raising this issue with the xUnit team, suggesting a ThrowsAsync be added.
An async delegate in this case is returning Task or Task<T>, and the ArgumentNullException is not thrown out of the delegate directly; instead, it is placed on the Task (Task.Exception.InnerException). Assert.Throws is expecting the exception to be thrown out of the delegate directly, not placed on a property of the return value.
You can create your own AssertEx.ThrowsAsync as such:
public static async Task ThrowsAsync<TException>(Func<Task> func)
{
var expected = typeof(TException);
Type actual = null;
try
{
await func();
}
catch (Exception e)
{
actual = e.GetType();
}
Assert.Equal(expected, actual);
}
which can be used as such:
[Theory, AutoWebData]
public async Task SearchWithNullQueryThrows(
SearchService sut,
CancellationToken dummyToken)
{
// Fixture setup
// Exercise system and verify outcome
await AssertEx.ThrowsAsync<ArgumentNullException>(async () =>
await sut.SearchAsync(null, dummyToken));
// Teardown
}
I use a similar approach in MSTest.
If you also need to return the exception to verify it then this might be useful:
public static async Task<Exception> AssertThrowsAsync<TException>(Func<Task> func)
{
var expected = typeof (TException);
Exception exception = null;
Type actual = null;
try
{
await func();
}
catch (Exception e)
{
actual = e.GetType();
exception = e;
}
Assert.NotNull(exception);
Assert.Equal(expected, actual);
return exception;
}

Including exception details when using a Castle WCF Facility hosted service

I'm having some trouble unit testing a bit of code while utilising the Wcf Facility for Castle Windsor. It seems to refuse to include Exception Details when an Exception is thrown, I only get to see empty FaultExceptions. This is my test setup:
First, here's a stub of the service that I will be connecting to:
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public abstract class StubAilDataService : IAilDataService
{
public virtual Method1()
{
}
/* More methods */
}
Notice that I've specified IncludeExceptionDetailsInFaults and set it to true.
This is how I host the stubbed service:
private ServiceHost _host;
private StubAilDataService _rhinoService;
[TestFixtureSetUp]
public void FixtureSetup()
{
var sba = new ServiceDebugBehavior {IncludeExceptionDetailInFaults = true};
_rhinoService = MockRepository.GeneratePartialMock<StubAilDataService>();
_host = new ServiceHost(_rhinoService);
_host.AddServiceEndpoint(typeof(IAilDataService), new WSHttpBinding("wsSecure"), "http://localhost:8080/Service");
_host.Open();
_container.AddFacility<WcfFacility>().Register(
Component.For<IServiceBehavior>().Instance(sba),
Component.For<IAilDataService>()
.LifeStyle.PerWcfSession()
.ActAs(new DefaultClientModel
{
Endpoint =
WcfEndpoint.BoundTo(new WSHttpBinding("wsSecure"))
.At("http://localhost:8080/Service")
}) // More stuff
);
}
I've done a PartialMock in an attempt to keep the Include.. attribute on the mocked object.
And the test. Notice that I tell my mocked service to throw a very specific exception here.
[Test]
[ExpectedException(typeof(AggregateException))]
public void AnalyzeProductCreationJobs_Should_Throw_Aggregate_Exception_If_A_DataService_Call_Throws()
{
//Arrange
_rhinoService.Expect(
s => s.CategoryIsInAgility(Arg<string>.Matches(str => str.Equals("000103")), Arg<Settings>.Is.Anything))
.Throw(new FaultException<InvalidOperationException>(new InvalidOperationException("FAIL!")));
var product = new Product { CategoryCode = "000103" };
var analyzer = TypeResolver.Resolve<ProductAnalyzer>();
//Act
analyzer.AnalyzeProductCreationJobs(product);
}
And finally, the code I'm actually testing:
public class ProductAnalyzer
{
private readonly IDataServiceClient _dataClient;
public ProductAnalyzer(IDataServiceClient dataClient)
{
_dataClient = dataClient;
}
public IEnumerable<IAdsmlJob<CreateResponse>> AnalyzeProductCreationJobs(Product product)
{
IList<IAdsmlJob<CreateResponse>> creationJobs = new List<IAdsmlJob<CreateResponse>>();
var task = Task.Factory.StartNew(() =>
{
// This is where the exception set up in my .Expect gets thrown.
bool categoryIsInAgility = _dataClient.CategoryIsInAgility(product.CategoryCode);
// Logic
}); // Continued by more tasks
try
{ task.Wait(); }
catch (AggregateException ae)
{
ae.Flatten().Handle(ex => ex is TaskCanceledException);
}
}
I would expect the service to crash and throw the exception I've set it up to throw - but the Wcf Facility seems to strip away the exception that is thrown and replace it with an empty FaultException instead.
Am i missing something? There are quite a few components working together here - and I'm not 100% sure where things go wrong.
You have to explicitly declare the type of fault exception the method throws on the interface.
Example:
[ServiceContract(Namespace = "http://www.example.com")]
public interface IAilDataService
{
[OperationContract]
[FaultContract(typeof(OperationPermissionFault))]
[FaultContract(typeof(InvalidOperationException))]
void Method1();
}
See http://msdn.microsoft.com/en-us/library/system.servicemodel.faultcontractattribute.aspx

Categories