I have a Mock object whose method I'm trying to setup by throwing an Exception when executed, for a particular unit test case using the Moq framework.
var mockMySvc = new Mock<IMySvc>();
mockMySvc
.Setup(x=>x.SomeMethod())
.Throws<Exception>();
//Execution of some code
//At the assertions
mockMySvc.VerifyAll();
At runtime, the code complains about all the expections of mockMySvc not having been met despite the exception being thrown. Am I missing something or does the .VerifyAll() method not work with the .Throws() functionality.
I don't know about your way of setting it up, but I always do it this way:
Assert.Throws<Exception>(() => myclass.SomeMethod());
This way you don't need to verify anything.
Based on your comment this is how you make sure the exception is thrown inside the method, so that you can check your code inside catch blocks.
[Test]
public void Test1()
{
_filmService.Setup(f => f.FindById(It.IsAny<int>())).Throws<Exception>();
_filmController.Test();
_filmService.Verify(f => f.Exists(It.IsAny<Film>()), Times.Once);
}
Actual code:
public ActionResult Test()
{
try
{
_filmService.FindById(-1);
}
catch (System.Exception)
{
_filmService.Exists(null);
}
return View();
}
This is just an example I tested in my code and it works correctly.
Related
Here is my controller method
[HttpPost]
[Authorize]
[Route(RouteConfig.Routes.LovList.contactStatus)]
public IHttpActionResult ContactStatusList()
{
try
{
var result = new DBClass.HeroDb().GetList(
DBClass.DBConstants.ListConstants.query_Contact_Status);
return Json(new Models.Response(
Models.ResponseMessages.Success,
result)
);
}
catch(System.Data.SqlClient.SqlException)
{
return InternalServerError();
}
catch(System.Exception ex)
{
Logger.Error(ex, ex.Message, ex.StackTrace);
return InternalServerError();
}
}
and this is my test case method
[TestMethod()]
public void ContactStatusListTest()
{
Mock<DBClass.HeroDb> mock = new Mock<DBClass.HeroDb>();
mock.Setup(x => x.GetList(DBClass.DBConstants.ListConstants.query_Contact_Status))
.Returns(CreateContactList());
var result = new ListController().ContactStatusList();
Models.Response response = (Models.Response)result;
Assert.AreEqual(response.Message, Models.ResponseMessages.Success);
Assert.IsNotNull(response.Data);
}
public System.Data.DataTable CreateContactList()
{
DataTable table = new DataTable();
table.Columns.Add("ContactStatus");
DataRow row1 = table.NewRow();row1["ContactStatus"] = "Contacted"; table.Rows.Add(row1);
DataRow row2 = table.NewRow(); row2["ContactStatus"] = "Not Contacted"; table.Rows.Add(row2);
DataRow row3 = table.NewRow(); row3["ContactStatus"] = "Contacted"; table.Rows.Add(row3);
return table;
}
I tried to mock GetList() function in my test method but it is not working. Controller method is giving an Internal server error . Because conrol is going to
var result = new DBClass.HeroDb()
.GetList(DBClass.DBConstants.ListConstants.query_Contact_Status);
this line and db object is null here. Please help as i am beginner in unit test case building .
First of all let's set the basis first: unit testing is different than integration testing.
In that case this is a unit test on the controller's method ContactStatusList. You're testing only this method and you actually did things correctly by trying to mock your HeroDb object. Note that you decided to mock this object because this is a dependency.
The problem is you set up the Mock but you don't use it because in your ContactStatusList method you call new DBClass.HeroDb().
There's a 2nd problem is that you're trying to mock a class. This is actually possible but all the class's methods you want to mock must be declared as virtual. Therefore it's actually better to mock an interface instead.
This interface should be received in the constructor of your ListController. On a regular execution of your Web Project inject an instance of that interface in the startup but in unit tests feed your mock to the ListController's constructor.
Remember this rule: Any dependency should be received by your controller's constructor
Here is your interface and your DbHero class
public interface IDbHero
{
IEnumerable<Contact> GetList(QueryContactStatus status);
}
public class DbHero : IDbHero
{
public IEnumerable<Contact> GetList(QueryContactStatus status)
{
// Implementation here
}
}
Now here's your controller:
[ApiController]
[Route("api/[controller]")]
public class ListController: ControllerBase
{
private readonly IHeroDb _heroDb;
public ListController(IHeroDb heroDb)
{
_heroDb = heroDb ?? throw new ArgumentNullException(nameof(heroDb));
}
[HttpPost]
[Authorize]
[Route(RouteConfig.Routes.LovList.contactStatus)]
public IHttpActionResult ContactStatusList()
{
try
{
var result = _heroDb.GetList(DBClass.DBConstants.ListConstants.query_Contact_Status);
return Json(new Models.Response(
Models.ResponseMessages.Success,
result)
);
}
catch(System.Exception ex)
{
Logger.Error(ex, ex.Message, ex.StackTrace);
throw;
}
}
}
Note that I removed the block where you only catch the SqlException because anyway if you've an unhandled exception the server will return an internal server error so it's useless to catch it if you don't even log the error.
Also in the 2nd catch block I just throw so the server will also automatically return an internal server error. If you're in debug mode this could be handy as you'd get the full Exception returned to you but if you return InternalServerError() you'd get no information even in debug and you'd have to check the logs...
In the ConfigureServices method of your Startup.cs class, inject your implementation of the IDbHero interface. Note this is a scoped service, meaning a new instance will be created for each HTTP request. Personally I never inject my Database access layer as a singleton because this could lead to some issues depending of the way this layer is implemented. For exemple EF Core's DbContext is incompatible with a singleton pattern.
services.AddScoped<IDbHero>(_ => new DBClass.HeroDb(Configuration.GetConnectionString("DbHeroConnectionString")));
I don't know how you handle the connection with the database because there's no mention of the connection string in your code example but I would do something like above.
Your connection string is coming from your appsettings.json config file
"ConnectionStrings": {
"DbHeroConnectionString": "YourConnectionString"
}
Now to use your mocked object in your unit test just do like this:
[TestMethod()]
public void ContactStatusList_ShouldReturnData_WhenCalled()
{
// ARRANGE
var mock = new Mock<IHeroDb>();
mock.Setup(x => x.GetList(DBClass.DBConstants.ListConstants.query_Contact_Status))
.Returns(CreateContactList());
var sut = new ListController(mock.Object);
// ACT
var result = sut.ContactStatusList();
// ASSERT
Models.Response response = (Models.Response)result;
Assert.AreEqual(response.Message, Models.ResponseMessages.Success);
Assert.IsNotNull(response.Data);
}
Notice few things here:
Your unit test name: This should show 3 things:
What you're testing (your method)
What should be the result (a result with data, a result with error, an exception raised...etc)
Under which conditions this result should happen
You can for exemple test that a method is returning an error when parameters have incorrect values. This should tested in another unit test
Unit tests are always in 3 parts ARRANGE, ACT and ASSERT. It's always a good practice to write that in each test so you can better organize your code
sut means System Under Test: this is what you want to test, all other dependencies (like your DbHero layer) should be mocked.
Now the next step would be to write a unit test to test your DbHero.GetList.
This time you'll create a real instance (not a mock) of the DbHero class because this is what you want to test: this is your sut.
Note that I've an intermediate level in testing so what I'm showing you is good practices I've learnt from my coworkers. But maybe some more experience developers could come up with better practices than mine.
I'm working on a project where I have to use something like soft assertion in NUnit in C#.
I need to implement a feature which says that if a test failed, go to the next test without showing exception but note that this test was failed.
I know that it's inadvisable to use multiple asserts but it's necessary because I got a form where f.e field with surname can fail but next tests are independent and should still run.
public class SoftAssertionTest
{
public static void AreEqual(object expected, object actual)
{
try
{
Assert.AreEqual(expected, actual);
}
catch (Exception e)
{
//Catch exception but remember that test failed and got to the
//next
}
}
}
Expected result is that all tests run without exceptions but finally result show Fail status.
[TestFixture]
public class TestClass
{
[Test]
public static void Test()
{
SoftAssertionTest.AreEqual(1, 2);
SoftAssertionTest.AreEqual(3, 4);
}
}
Any ideas?
NUnit 3 has Assert.Warn if you just want to warn on failures and Assert.Multiple if you want to run multiple asserts and fail the test if any of the individual asserts fail, but ensure that all of the asserts are run.
Try SoftAssertion
SoftAssert softAssert = new SoftAssert();
softAssert.True(false);
softAssert.False(true);
softAssert.NotNull(null);
softAssert.VerifyAll();
Consider the following highly simplified viewmodel for fetching and showing a list of projects:
public class ProjectListViewModel
{
private readonly IWebService _webService;
public ICommand RefreshCommand { get; }
// INotifyPropertyChanged implementation skipped for brevity
public ObservableCollection<Project> Projects { get; set; }
public ProjectListViewModel(IWebService serverApi)
{
_serverApi = serverApi;
// ICommand implemented by Xamarin.Forms
RefreshCommand = new Command(async () => await RefreshAsync());
}
private async Task RefreshAsync()
{
try
{
Projects = await _webService.GetProjectsAsync();
}
catch (TaskCanceledException)
{
// Empty (task only cancelled when we are navigating away from page)
}
}
}
Using NUnit and Moq, I'm trying test that when GetProjectsAsync throws a TaskCanceledException, the ViewModel will catch it. The closest I get is this:
[Test]
public void When_Refreshing_Catches_TaskCanceledException()
{
// Arrange
webService = new Mock<IServerApi>();
webService.Setup(mock => mock.GetProjectsAsync())
.ThrowsAsync(new TaskCanceledException());
vm = new ProjectListViewModel(webService.Object);
// Act and assert
Assert.That(() => vm.RefreshCommand.Execute(null), Throws.Nothing);
}
The test passes, but unfortunately it's faulty - it still passes if I throw e.g. Exception instead of TaskCanceledException. As far as I know, the reason is that the exception doesn't bubble up past the command lambda, async () => await RefreshAsync(), so no exception thrown by GetProjectsAsync will ever be detected by the test. (When running the actual app however, the TaskCanceledException will bubble up and crash the app if not caught. I suspect this is related to synchronization contexts, of which I have very limited understanding.)
It works if I debug the test - if I mock it to throw Exception, it will break on the line with the command/lambda definition, and if I throw TaskCanceledException, the test will pass.
Note that the results are the same if I use Throws instead of ThrowsAsync. And in case it's relevant, I'm using the test runner in ReSharper 2016.2.
Using nUnit, is it possible at all to unit test exceptions thrown when executing "async" commands like this? Is it possible without writing a custom Command implementation?
Your problem is here:
new Command(async () => await RefreshAsync())
This async lambda is converted to an async void method by the compiler.
In my article on async best practices, I explain why the exception cannot be caught like this. async methods cannot propagate their exceptions directly (since their stack can be gone by the time the exception happens). async Task methods solve this naturally by placing the exception on their returned task. async void methods are unnatural, and they have nowhere to place the exception, so they raise it directly on the SynchronizationContext that was current at the time the method started.
In your application, this is the UI context, so it's just like it was thrown directly in an event handler. In your unit test, there is no context, so it's thrown on a thread pool thread. I think NUnit's behavior in this situation is to catch the exception and dump it to the console.
Personally, I prefer using my own asynchronous-compatible ICommand such as AsyncCommand in my Mvvm.Async library (also see my article on asynchronous MVVM commands):
new AsyncCommand(_ => RefreshAsync())
which can then be naturally unit tested:
await vm.RefreshCommand.ExecuteAsync(null); // does not throw
Alternatively, you can provide your own synchronization context in the unit test (using, e.g., my AsyncContext):
// Arrange
webService = new Mock<IServerApi>();
webService.Setup(mock => mock.GetProjectsAsync())
.ThrowsAsync(new TaskCanceledException());
vm = new ProjectListViewModel(webService.Object);
// Act/Assert
AsyncContext.Run(() => vm.RefreshCommand.Execute(null));
In this case, if there was an exception, Run would propagate it.
Since async void (which is what the handler to your command is) is basically "fire and forget" and you can't await for it in the test I would suggest unit testing the RefreshAsync() method (you may want to make it internal or public), this can be easily done in NUnit:
if you are asserting exceptions being thrown:
[Test]
public async Task Test_Exception_RefreshAsync(){
try
{
await vm.RefreshAsync();
Assert.Fail("No exception was thrown");
}
catch (NotImplementedException e)
{
// Pass
}
}
or simply
[Test]
public async Task Test_RefreshAsync(){
var vm = new ProjectListViewModel(...);
await vm.RefreshAsync();
//Assertions here
}
or as other answer state you can create your own AsyncCommand that you can await on.
I have catered for the situation when a config key has not been set in my business logic as follows:
public class Presenter
{
private readonly IView view;
public Presenter(IView view)
{
this.view = view;
}
public void DoStuff()
{
try
{
String someKey = ConfigurationManager.AppSettings["SomeKey"].ToString();
if (string.IsNullOrEmpty(someKey))
{
throw new InvalidOperationException("SomeKey not set.");
}
// do stuff
}
catch (InvalidOperationException ex)
{
// provide view with friendly error
// log error
}
}
}
My attempt at testing that this error occurrs when the key is not set:
[TestMethod]
public void Presenter_DoStuff_Should_Throw_InvalidOperationException_When_SomeKey_Not_Supplied()
{
// Arrange
mockIView = new Mock<IView>();
presenter = new Presenter(mockIView.Object);
// Act
// Assert
// NUnit here as more precise
NUnit.Framework.Assert.Throws<InvalidOperationException>(() => presenter.DoStuff(), "SomeKey not set.");
}
How do I get my test to pass as it stands? It currently fails because the try-catch is swallowing the exception for logging purposes. The test passes without the try-catch. This is with the AppSettings["SomeKey"] manually set to empty string.
Secondly, how do I specify in the test that someKey in DoStuff is empty to actually test this situation without having to manually remove the key setting?
Any help is greatly appreciated as I'm new to unit testing.
First of all, your test is invalid by design because your method doesn't actually throw an exception to calling code. This is because you immediately catch and handle that exception. This is actually a very incorrect use of exceptions. There's no need to throw based on a condition and then immediately catch when all you logically need to do is check that condition. Something like this:
public void DoStuff()
{
var someKey = ConfigurationManager.AppSettings["SomeKey"];
if (string.IsNullOrEmpty(someKey))
{
// provide view with friendly error
// log error
return;
}
// do stuff
}
Now the question becomes... what are you testing? The actual business logic of this method is in:
// do stuff
So hopefully that's the critical focus of the testing. Now, in order to reach 100% code coverage, you do also need to test what's in that conditional block. In order to do that, you need to simulate the condition. However, you have an external dependency:
ConfigurationManager
In order to test the logic, you're going to need to mock that dependency. A general approach to this is to create a kind of wrapper object for the dependency. In this specific case it could be something as simple as:
public class ConfigurationWrapper
{
public virtual string SomeKey
{
get
{
return ConfigurationManager.AppSettings["SomeKey"];
}
}
}
This could be separate from the class that has DoStuff, or even nested within. Depends on where/how else you'd want to use it. And of course it can be extended to wrap other configuration dependencies. Then within the class which has DoStuff you'd create a property for this wrapper. Something like this:
private ConfigurationWrapper _config;
public ConfigurationWrapper Configuration
{
get
{
if (_config == null)
_config = new ConfigurationWrapper();
return _config;
}
set { _config = value; }
}
And use it in DoStuff():
var someKey = this.Configuration.SomeKey;
Now the dependency on ConfigurationManager is wrapped in a mockable object. So in your unit test you'd create a mock ConfigurationWrapper object and set it on the object being tested. Something like:
var mockConfig = new Mock<ConfigurationWrapper>();
presenter.Configuration = mockConfig;
You can set your mock to return either a valid value or an empty string for the .SomeKey property, depending on what any given test needs. And then you'd validate whatever side-effect is produced by the conditional statement. (The "friendly error message" and the "logging" I assume. Which may involve further mocks, I can't know at this time.)
Of course, to reach 100% coverage you'd also need to add another test for the default case of the wrapper when one isn't externally set. That should be a fairly simple test:
// arrange
// no mocks to set up
// act
var presenter = new Presenter(null);
// assert
Assert.IsNotNull(presenter.Configuration);
I wrote a unit test in such a way that it should throw AnException or AnotherException, both deriving from AnExceptionBaseException. I then proceeded to add an ExpectedExceptionAttribute for the base exception, only to find that my test will still be marked as failed.
Test Name: Call_Should_Throw_If_Device_Is_Not_Ready Test
...
Result Message: Test method
DiskManagementTests.DiskFreeSpaceTests.Call_Should_Throw_If_Device_Is_Not_Ready
threw exception System.IO.FileNotFoundException, but exception
System.IO.IOException was expected. Exception message:
System.IO.FileNotFoundException: The device is not ready. (Exception
from HRESULT: 0x80070015)
This seems like a reasonable design decision because, in this particular case, the exception is generated from an HRESULT return code. That makes it nearly impossible to determine which exception will be thrown. At least not without copying the code logic from the unit that my test is supposed to ...test.
My code (I believe this can throw either FileNotFound or DirectoryNotFound):
[TestMethod]
[ExpectedException(typeof(IOException))]
public void Call_Should_Throw_If_Device_Is_Not_Ready()
{
foreach (DriveInfo drive in DriveInfo.GetDrives().Where(drive => !drive.IsReady))
{
DiskFreeSpace diskFreeSpace = DiskManagement.GetDiskFreeSpace(drive.RootDirectory.FullName);
Assert.Fail("API call did not fail even though the drive reports that it is not ready.");
}
Assert.Inconclusive("All drives were ready. Try testing with an empty disc drive.");
}
Do I need to reconsider the way I write unit tests?
EDIT
This scenario is supported after all. All it really took was setting AllowDerivedTypes to true.
[TestMethod]
[ExpectedException(typeof(IOException), AllowDerivedTypes = true)]
public void Call_Should_Throw_If_Device_Is_Not_Ready()
{
// ...
}
You can create your own ExpectedException attribute that will check if the thrown exception inherites the Exception defined in the attribute.
public sealed class MyExpectedException : ExpectedExceptionBaseAttribute
{
private Type _expectedExceptionBaseType;
public MyExpectedException(Type expectedExceptionType)
{
_expectedExceptionBaseType = expectedExceptionType;
}
protected override void Verify(Exception exception)
{
Assert.IsNotNull(exception);
Assert.IsTrue(exception.GetType().IsInstanceOfType(typeof(_expectedExceptionBaseType)) ||
exception.GetType().IsSubclassOf(typeof(_expectedExceptionBaseType)));
}
}
and change the attribute to your test:
[MyExpectedException(typeof(IOException))]