unit test exception handled by try catch - c#

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);

Related

Unit test for void method with Interface as parameter

New to Unit testing, I have below sample code and I want to create a unit test for this , Please suggest what should i do to create a unit test for this ? any link or pointers would be helpful to start
public class UserNotification : Work
{
public override void Execute(IWorkContext iwc)
{
throw new InvalidWorkException($"some message:{iwc.Name} and :{iwc.Dept}");
}
}
Edit: using MSTest for Unit testing
First, you need a test project alongside with your regular project.
You can pick from these three:
MSTest
nUnit
xUnit
All of these should have a project template in VS2022.
xUnit is a popular one, so let's pick that. The usual naming convention for test projects is YourProject.Tests. Rename UnitTest1.cs class to UserNotificationTests.cs.
As simple as it gets, you can now start writing your tests. In xUnit, a method with [Fact] attribute is a test method.
using Xunit;
namespace MyProject.Tests
{
public class UserNotificationTests
{
[Fact]
public void Execute_Should_Throw_InvalidWorkException_With_Message()
{
}
}
}
Don't think these methods as the methods in the code, naming should be close to English sentences and should reveal the intent as a regular sentence.
Classic approach to unit testing has three phases:
Arrange: Take instances of your objects, set your expected output, mock dependencies, make them ready.
Act: Call the actual action you want to test.
Assert: Check if how your actual output relates to your expected output.
Let's start with arranging.
We need a new instance of UserNotification class so we can call Execute().
We need any dummy IWorkContext object so we can pass it. We'll use NSubstitute library for that.
// Don't forget to add using NSubstitute
// Arrange
var userNotification = new UserNotification();
var workContext = Substitute.For<IWorkContext>();
workContext.Name = "testName";
workContext.Dept = "testDept";
Now you act, and invoke your method:
// Act
Action act = () => userNotification.Execute(workContext);
And lastly we assert. I highly recommend FluentAssertations library for asserting.
// Assert
act.Should().Throw<InvalidWorkException>()
.WithMessage($"some message:{workContext.Name} and :{workContext.Dept}");
Navigate to View > Test Explorer and run your tests, you should see something similar to this:
Congratulations, you wrote your first unit test.
Here's the final version of your test code:
using FluentAssertions;
using NSubstitute;
using System;
using Xunit;
namespace MyProject.Tests
{
public class UserNotificationTests
{
[Fact]
public void Execute_Should_Throw_InvalidWorkException_With_Message()
{
// Arrange
var userNotification = new UserNotification();
var workContext = Substitute.For<IWorkContext>();
workContext.Name = "testName";
workContext.Dept = "testDept";
// Act
Action act = () => userNotification.Execute(workContext);
// Assert
act.Should().Throw<InvalidWorkException>()
.WithMessage($"some message:{workContext.Name} and :{workContext.Dept}");
}
}
public class UserNotification : Work
{
public override void Execute(IWorkContext iwc)
{
throw new InvalidWorkException($"some message:{iwc.Name} and :{iwc.Dept}");
}
}
public abstract class Work
{
public virtual void Execute(IWorkContext iwc) { }
}
public interface IWorkContext
{
public string Name { get; set; }
public string Dept { get; set; }
}
public class InvalidWorkException : System.Exception
{
public InvalidWorkException() { }
public InvalidWorkException(string message) : base(message) { }
public InvalidWorkException(string message, System.Exception inner) : base(message, inner) { }
protected InvalidWorkException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
}
}
Writing tests feels a lot different than writing regular code. But in time you'll get the hang of it. How to mock, how to act, how to assert, these may vary depending on what you are testing. The main point is to isolate the main thing you want to unit test, and mock the rest.
Good luck!
Because your title mentions specifically that you're trying to test a method with a void return type; I infer that you've already been testing methods with actual return values, and therefore that you already have a test project and know how to run a test once it is written. If not; the answer written by Mithgroth is a good explanation on how to get started on testing in general.
Your test is defined by the behavior that you wish to test. Your snippet has no behavior, which makes it hard to give you a concrete answer.
I've opted to rewrite your example:
public class UserNotification : Work
{
public override void Execute(IWorkContext iwc)
{
var splines = iwc.GetSplines();
iwc.Reticulate(splines);
}
}
Now we have some behavior that we want to test. The test goal is to answer the following question:
When calling Execute, does UserNotification fetch the needed splines and reticulate them?
When unit testing, you want to mock all other things. In this case, the IWorkContext is an external dependency, so it should be mocked. Mocking the work context allows us to easily configure the mock to help with the testing. When we run the test, we will pass an IWorkContext object which acts as a spy. In essence, this mocked object will:
... have been set up to return a very specific set of splines, one that we chose for the test's purpose.
... secretly record any calls made to the Reticulate method, and tracks the parameters that were passed into it.
Before we get into the nitty gritty on how to mock, we can already outline how our test is going to go:
[Test]
public void ReticulatesTheContextSplines()
{
// Arrange
IWorkContext mockedContext = ...; // This comes later
UserNotification userNotification = new UserNotification();
// Act
userNotification.Execute(mockedContext);
// Assert
// Confirm that Reticulate() was called
// Confirm that Reticulate() was given the result from `GetSplines()`
}
There's your basic unit test. All that's left is to create our mock.
You can write this yourself if you want. Simply create a new class that implements IWorkContext, and give it some more public properties/methods to help you keep track of things. A very simple example would be:
public class MockedWorkContext : IWorkContext
{
// Allows the test to set the returned result
public IEnumerable<Spline> Splines { get; set; }
// History of arguments used for calls made to Reticulate.
// Each call will add an entry to the list.
public List<IEnumerable<Spline>> ReticulateArguments { get; private set; } = new List<IEnumerable<Spline>>();
public IEnumerable<Spline> GetSplines()
{
// Returns the preset splines that the test configured
return this.Splines;
}
// Mocked implementation of Reticulate()
public void Reticulate(IEnumerable<Spline> splines)
{
// Does nothing except record what you passed into it
this.ReticulateArguments.Add(splines);
}
}
This is a very simplified implementation, but it gets the job done. The test will now look like this:
[Test]
public void ReticulatesTheContextSplines()
{
// Arrange
IEnumerable<Spline> splines = new List<Spline>() { new Spline(), new Spline() }; // Just create some items here, it's random test data.
IWorkContext mockedContext = new MockedWorkContext();
mockedContext.Splines = splines;
UserNotification userNotification = new UserNotification();
// Act
userNotification.Execute(mockedContext);
// Assert - Confirm that Reticulate() was called
mockedContext.ReticulateArguments.Should().HaveCount(1);
// Confirm that Reticulate() was given the result from `GetSplines()`
mockedContext.ReticulateArguments[0].Should().BeEquivalentTo(splines);
}
This test now exactly tests the behavior of your method. It uses the mocked context as a spy to report on what your unit under test (i.e. UserNotification) does with the context that you pass into it.
Note that I am using FluentAssertions here, as I find it the most easily readable syntax. Feel free to use your own assertion logic.
While you can write your own mocks; there are mocking libraries that help cut down on the boilerplating. Moq and NSubstitute are the two biggest favorites as far as I'm aware. I personally prefer NSubstitute's syntax; but both get the job done equally well.
If you want to use nunit the documentation with example is pretty easy to follow, link below.
Nunit documentation
And I think all other unit test framework have something similar to this.
[Test]
public void Execute_WhenCalled_ThrowArgumentException()
{
//Initialize an instance of IWorkContext
var iwc = new WorkContext();
//or use a Mock object, later on in assert use
//userNotification.Execute(iwc.Object)
var iwc = new Mock<IWorkContext>();
var userNotification = new UserNotification();
Assert.Throws(typeof(InvalidWorkException), () =>
{
userNotification.Execute(iwc)
});
}

Unit Testing a method which contains a Using block

I have a already written (was written years ago) C# function, I have been asked to cover this method with Unit Tests.
public string PlaceOrder(int requestId, string orderedby)
{
try
{
using (DatabaseContext dbContext = new DatabaseContext("myConnectionStringHere"))
{
var req = dbContext.Orders.Where(row => row.id == requestId).FirstOrDefault();
if (req == null)
return "not found";
req.status="A";
dbContext.SaveChanges();
return "found";
}
}
catch (Exception ex)
{
return "error";
}
}
Now while Unit testing I need to make sure that it does not write anything to database, so I have to MOQ it.
How can I MOQ, it contains Using block.
I know architecture could have been better and design patterns should have been followed but I am not allowed to change the structure of the application as it is a legacy application.
The general guidance here is to prefer Integration Test with in memory (w/o sqlite) database over unit testing.
Let me suggest you four helper libraries which can make your testing easier:
EntityFrameworkCoreMock
Github link
The prerequisite here is to mark your DbSet as virtual like this:
public virtual DbSet<Order> Orders { get; set; }
Then you can create a mock where you can populate your Orders collection with some dummy data:
var initialOrders = new[]
{
new Order { ... },
new Order { ... },
};
var dbContextMock = new DbContextMock<DatabaseContext>(new DbContextOptionsBuilder<DatabaseContext>().Options);
var ordersDbSetMock = dbContextMock.CreateDbSetMock(db => db.Orders, initialOrders);
You have to rewrite your containing class of the PlaceOrder method in a way to receive a DatabaseContext parameter in the constructor to be able inject dbContextMock.Object during testing.
In the assertion phase you can query your data and make assertion against it. Since you do not call Add, Remove or any other CRUD method, you can only Verify the SaveChanges call.
public void GivenAnExistingOrder_WhenICallPlaceOrder_ThenSaveChangesIsCalledOnce()
{
...
//Assert
dbMock.Verify(db => db.SaveChanges(), Times.Once);
}
public void GivenANonExistingOrder_WhenICallPlaceOrder_ThenSaveChangesIsCalledNever()
{
...
//Assert
dbMock.Verify(db => db.SaveChanges(), Times.Never);
}
EntityFrameworkCore.Testing
Github link
It is working more or less in the way as the previous library.
var dbContextMock = Create.MockedDbContextFor<DatabaseContext>();
dbContextMock.Set<Order>().AddRange(initialOrders);
dbContextMock.SaveChanges();
The assertions work in the same way.
A 3rd (less mature) library is called Moq.EntityFrameworkCore.
If you really keen to perform unit testing by avoiding in memory database then you should give a try to the MockQueryable library.
const int requestId = 1;
var orders = new List<Order>();
var ordersMock = orders.AsQueryable().BuildMockDbSet();
ordersMock.Setup(table => table.Where(row => row.Id == requestId)).Returns(...)
Here you are basically mocking what should be the result of your Where filter. In order to be able to use this the containing class of the PlaceOrder should receive a DbSet<Order> parameter via its constructor.
Or if you have an IDatabaseContext interface then you can use that one as well like this:
Mock<IQueryable<Order>> ordersMock = orders.AsQueryable().Build();
Mock<IDatabaseContext> dbContextMock = ...
dbContextMock.Setup(m => m.ReadSet<Order>()).Returns(ordersMock.Object));
Many things should be changed here:
1:
Do not implement your connection string this way, directly in the code base.
Instead, DI your database into your classes.
so this pseudo code should help out with the general idea.
public void ConfigureService(IServiceCollection serviceCollection)
{
...
string connectionString = //secure storage;
serviceCollection.AddDbContext<DatabaseContext>(options => {
options.UseSqlServer(connectionString);
});
...
}
And then
public class OrderRepository
{
private IServiceScopeFactory _serviceScopeFactory ;
public OrderRepository(IServiceScopeFactory serviceScopeFactory ){
_serviceScopeFactory = serviceScopeFactory ;
}
...
public string PlaceOrder(int requestId, string orderedby)
{
try
{
using (var context = serviceScopeFactory.CreateScope())
{
var req = context.Orders.Where(row => row.id == requestId).FirstOrDefault();
if (req == null)
return "not found";
req.status="A";
context.SaveChanges();
return "found";
}
}
catch (Exception ex)
{
return "error";
}
}
...
}
if you want to make an integration test, you can then use an InMemory db to emulate whatever you want.
Or you can connect to a "real" db, and do it that way.
If you want to make it a unit test, you can see at this link:
How to setup a DbContext Mock
2:
returning a string saying found/not found for a order being placed, seems extremely counter productive.
if your aim is to log this information, provider a DI logger, that can log this. (Try importing the ILogger interface, it's a microsoft extension on logging, can't remember the nuget package name)
should enable you to log with DI very efficiently.
If your aim is to let a possible UI display this message, there is no way the message content should originate from back-end or domain logic.
At least not like this.
Then you should make an interface for a response, and return an implementation of said interface, that exists somewhere else as a minimum but even that is a bit like peeing your pants.
(And contains a UI friendly message, can contain a possible stacktrace/exception), and other possible relevant information, like what Id you were trying to place an order on etc.)
You should make it something that happens at the interface between your UI and domain logic, provided that is what the string is intended for. Where you would expect to see error handling.
3:
WTF is up with the catch? you just return error? Well ? What error? you lose the stack-trace this way?
Someone should be punished for that.

Database function Mock is not working in c# web api unit testing

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.

Unti Test: Mock a method result

public void Pay()
{
// some insert db code
// ...
// Call Bank api
BankApi api = new BankApi();
int result = api.pay();
if(result == 1)
{
//...
}
else
{
//...
}
}
I dont want to call api in unit test. How to mock the pay method without modify inner code (such as the line new BankApi() code)?
Its possible to mock your BankApi class without changing any of your legacy code, you just need a unit testing framework that allows you to mock concrete classes.
for example a test for your method with Typemock :
[TestMethod]
public void ExampleTest()
{
//fakes the next BankApi instace
var handler = Isolate.Fake.NextInstance<BankApi>();
//change the pay method behavior
Isolate.WhenCalled(() => handler.pay()).WillReturn(1);
new ClassUnderTest().Pay();
}
First, as stated, you should create an Interface.
public interface IBankApi
{
int pay();
}
Then, what you can do is mock this interface like this (I'm using Moq "Mock you" here, you will need to add the NuGet package "Moq" as reference to your application, and you could use other mocking libraries of course)
apiMock = new Mock<IBankApi>();
just after that you will tell what this call should return (that would be actual mocking)
apiMock.Setup(x => x.pay()).Returns(1); //
Then, this api "pseudo object", can be used by using apiMock.Object
Now , this information I just gave you doesn't directly solve your problem.
As stated in the comments, you need a better uncoupling of your code.
You need, for example, some kind of "dependency injection" to allow for such a uncoupling.
Here is a simple example of how it can be done :
public class ClassThatUsesYourBankApi
{
private readonly IBankApi _api;
// the constructor will be given a reference to the interface
public ClassThatUsesYourBankApi (IBankApi api)
{
// here you could check for null parameter and throw exception as needed
this._api = api;
}
// this method can now be tested with the mock interface
public void MethodThatUseTheApi()
{
int result = this._api.pay();
if (result == 1)
{
// some things that happens
}
else
{
// some other thing
}
}
}
How to unit test that method :
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
[TestClass]
public class TestMyMethod
{
[TestMethod]
public void MyMethod_WithBankApiReturns1_ShouldHaveThingsThatHappens()
{
// Arrange
var apiMock = new Mock<IBankApi>();
apiMock.Setup(api => api.pay())
.Returns(1);
var myObject = new ClassThatUsesYourBankApi(apiMock.Object);
// Act
int result = myObject.MethodThatUseTheApi();
// Assert
// Here you test that the things that should have happened when the api returns 1 actually have happened.
}
}
The key thing to understand here, is that you must not instantiate the api you need to mock in the method you want to test
In other words, "uncoupling" your method with your api is done by programming to an interface, and code such as you don't have
var api = new BankApi()
directly in the method you want to unit test.
I showed a way to do that, and there are other.

Unit Testing a controller that uses windows authentication

-------Please see updates below as I now have this set up for dependency injection and the use of the MOQ mocking framework. I'd still like to split up my repository so it doesn't directly depend on pulling the windowsUser within the same function.
I have a Web API in an intranet site that populates a dropdown. The query behind the dropdown takes the windows username as a parameter to return the list.
I realize I don't have all of this set up correctly because I'm not able to unit test it. I need to know how this "should" be set up to allow unit testing and then what the unit tests should look like.
Additional info: this is an ASP.NET MVC 5 application.
INTERFACE
public interface ITestRepository
{
HttpResponseMessage DropDownList();
}
REPOSITORY
public class ExampleRepository : IExampleRepository
{
//Accessing the data through Entity Framework
private MyDatabaseEntities db = new MyDatabaseEntities();
public HttpResponseMessage DropDownList()
{
//Get the current windows user
string windowsUser = HttpContext.Current.User.Identity.Name;
//Pass the parameter to a procedure running a select query
var sourceQuery = (from p in db.spDropDownList(windowsUser)
select p).ToList();
string result = JsonConvert.SerializeObject(sourceQuery);
var response = new HttpResponseMessage();
response.Content = new StringContent(result, System.Text.Encoding.Unicode, "application/json");
return response;
}
}
CONTROLLER
public class ExampleController : ApiController
{
private IExampleRepository _exampleRepository;
public ExampleController()
{
_exampleRepository = new ExampleRepository();
}
[HttpGet]
public HttpResponseMessage DropDownList()
{
try
{
return _exampleRepository.DropDownList();
}
catch
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
}
}
}
UPDATE 1
I have updated my Controller based on BartoszKP's suggestion to show dependency injection.
UPDATED CONTROLLER
public class ExampleController : ApiController
{
private IExampleRepository _exampleRepository;
//Dependency Injection
public ExampleController(IExampleRepository exampleRepository)
{
_exampleRepository = exampleRepository;
}
[HttpGet]
public HttpResponseMessage DropDownList()
{
try
{
return _exampleRepository.DropDownList();
}
catch
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
}
}
}
UPDATE 2
I have decided to use MOQ as a mocking framework for unit testing. I'm able to test something simple, like the following. This would test a simple method that doesn't take any parameters and doesn't include the windowsUser part.
[TestMethod]
public void ExampleOfAnotherTest()
{
//Arrange
var mockRepository = new Mock<IExampleRepository>();
mockRepository
.Setup(x => x.DropDownList())
.Returns(new HttpResponseMessage(HttpStatusCode.OK));
ExampleController controller = new ExampleController(mockRepository.Object);
controller.Request = new HttpRequestMessage();
controller.Configuration = new HttpConfiguration();
//Act
var response = controller.DropDownList();
//Assert
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
I need help testing the DropDownList method (one that does include code to get the windowsUser). I need advice on how to break this method apart. I know both parts shouldn't been in the same method. I don't know how to arrange splitting out the windowsUser variable. I realize this really should be brought in as a parameter, but I can't figure out how.
You usually do not unit-test repositories (integration tests verify if they really persist the data in the database correctly) - see for example this article on MSDN:
Typically, it is difficult to unit test the repositories themselves, so it is often better to write integration tests for them.
So, let's focus on testing only the controller.
Change the controller to take IExampleRepository in its constructor as a parameter:
private IExampleRepository _exampleRepository;
public ExampleController(IExampleRepository exampleRepository)
{
_exampleRepository = exampleRepository;
}
Then, in your unit tests, use one of mocking frameworks (such as RhinoMock for example) to create a stub for the sole purpose of testing the controller.
[TestFixture]
public class ExampleTestFixture
{
private IExampleRepository CreateRepositoryStub(fake data)
{
var exampleRepositoryStub = ...; // create the stub with a mocking framework
// make the stub return given fake data
return exampleRepositoryStub;
}
[Test]
public void GivenX_WhenDropDownListIsRequested_ReturnsY()
{
// Arrange
var exampleRepositoryStub = CreateRepositoryStub(X);
var exampleController = new ExampleController(exampleRepositoryStub);
// Act
var result = exampleController.DropDownList();
// Assert
Assert.That(result, Is.Equal(Y));
}
}
This is just a quick&dirty example - CreateRepositoryStub method should be of course extracted to some test utility class. Perhaps it should return a fluent interface to make the test's Arrange section more readable on what is given. Something more like:
// Arrange
var exampleController
= GivenAController()
.WithFakeData(X);
(with better names that reflect your business logic of course).
In case of ASP.NET MVC, the framework needs to know how to construct the controller. Fortunately, ASP.NET supports the Dependency Injection paradigm and a parameterless constructor is not required when using MVC unity.
Also, note the comment by Richard Szalay:
You shouldn't use HttpContext.Current in WebApi - you can use base.User which comes from HttpRequestBase.User and is mockable. If you really want to continue using HttpContext.Current, take a look at Mock HttpContext.Current in Test Init Method
One trick that I find very useful when trying to make old code testable when said code is accessing some global static or other messy stuff that I can't easily just parameterize is to wrap access to the resource in a virtual method call. Then you can subclass your system under test and use that in the unit test instead.
Example, using a hard dependency in the System.Random class
public class Untestable
{
public int CalculateSomethingRandom()
{
return new Random().Next() + new Random().Next();
}
}
Now we replace var rng = new Random();
public class Untestable
{
public int CalculateSomethingRandom()
{
return GetRandomNumber() + GetRandomNumber();
}
protected virtual int GetRandomNumber()
{
return new Random().Next();
}
}
Now we can create a testable version of the class:
public class Testable : Untestable
{
protected override int GetRandomNumber()
{
// You can return whatever you want for your test here,
// it depends on what type of behaviour you are faking.
// You can easily inject values here via a constructor or
// some public field in the subclass. You can also add
// counters for times method was called, save the args etc.
return 4;
}
}
The drawback with this method is that you can't use (most) isolation frameworks to implement protected methods (easily), and for good reason, since protected methods are sort of internal and shouldn't be all that important to your unit tests. It's still a really handy way of getting things covered with tests so you can refactor them, instead of having to spend 10 hours without tests, trying to do major architectual changes to your code before you get to "safety".
Just another tool to keep in mind, I find it comes in handy from time to time!
EDIT: More concretely, in your case you might want to create a protected virtual string GetLoggedInUserName(). This will technically speaking keep the actual call to HttpContext.Current.User.Identity.Name untested, but you will have isolated it to the simplest smallest possible method, so you can test that the code is calling the correct method the right amount of times with the correct args, and then you simply have to know that HttpContext.Current.User.Identity.Name contains what you want. This can later be refactored into some sort of user manager or logged in user provider, you'll see what suits best as you go along.

Categories