I'm new to C# and trying to write an integration test for a service. The service uses 2 providers as example below, and I don't know how to include them in my test. I'm using xUnit.
// Service
namespace App.Services
{
public interface IProvider
{ }
public class FirstProvider : IProvider
{ }
public class SecondProvider : IProvider
{ }
public class AppManager
{
private readonly IEnumerable<IProvider> _providers;
public AppManager(
IEnumerable<IProvider> providers)
{
_providers = providers;
}
public asyn Task ListItems()
{
foreach (var singleProvider in _providers) // When running test, I got ERROR here: _providers is null)
{
// do something
}
}
}
}
// Test
public class AppManagerTest
{
private readonly IEnumerable<IProvider> _providers;
[Fact]
public async ListItems()
{
// Arrange
var sut = new AppManager(_providers);
// Act
// Assert
}
}
When running test, I got an error as pointed above in my service code. The error is System.NullReferenceException : Object reference not set to an instance of an object. Debugging the test shows that _providers is null.
So as I understood, my test does not get any of the providers. What should I do here?
I'm working on creating unit tests using moq and I'm having trouble figuring out how to apply this framework to a portion of my program that deals with using an HttpClient. There's various resources I found that demonstrate how to mock an HttpClient response directly but the way my application makes use of HttpClient is slightly different with the utilization of Threads.
The test's skeleton:
public class MyTestClass
{
public void myTest()
{
ClassA classObj = new ClassA();
classObj.Start();
// I'd like to use moq somewhere here to mock the response that occurs in DoThreadStuff() below
}
}
The class under testing:
public class ClassA
{
private readonly Thread _myThread;
private HttpClient _client;
public ClassA()
{
// initialize some values
_myThread = new Thread(DoThreadStuff);
}
public void Start()
{
_myThread.Start(); // starts DoThreadStuff()
}
private void DoThreadStuff()
{
var newClient = getNewHttpClient(); // utility function returns a HttpClient
var response = newClient.GetAsync("/my/api/status/endpoint");
}
}
As you can see, when ClassA.Start() gets called, a new HttpClient gets created and used via GetAsync. What would the correct way to structure a test for this look like? Will I have to change the implementation of my existing classes to accommodate for Moq? Does anyone have experience with something very similar which I could take a look at?
Let's suppose that your ClassA looks like this:
public class ClassA
{
private readonly Thread _myThread;
public ClassA()
{
_myThread = new Thread(DoThreadStuff);
}
public void Start()
{
_myThread.Start();
}
private void DoThreadStuff()
{
var newClient = getNewHttpClient();
var response = newClient.GetAsync("https://httpstat.us//200").GetAwaiter().GetResult();
if(response.StatusCode == HttpStatusCode.OK)
Console.WriteLine("OK");
else if(response.StatusCode == HttpStatusCode.InternalServerError)
Console.WriteLine("Not good");
}
protected virtual HttpClient getNewHttpClient()
{
return new HttpClient();
}
}
For the sake of testability I've added some dummy code after the getNewHttpClient call
Please note that calling an async method in sync fashion (.GetAwaiter().GetResult()) is not really a good idea
I've also added the getNewHttpClient to your class as protected virtual to be able to overwrite it easily
Now let's create a helper method to be able to mock an HttpClient:
public static HttpClient SetupMockClient(HttpResponseMessage response)
{
var mockMessageHandler = new Mock<HttpMessageHandler>();
mockMessageHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(response);
return new HttpClient(mockMessageHandler.Object);
}
Let's derive from the ClassA to override the getNewHttpClient method
internal class VerifiableClassA : ClassA
{
private HttpClient mockedHttpClient;
public VerifiableClassA(HttpClient mockedHttpClient)
{
this.mockedHttpClient = mockedHttpClient;
}
protected override HttpClient getNewHttpClient()
{
return this.mockedHttpClient;
}
}
Please note that you can do this with moq as well so you don't need to introduce a new class just for testing. But in that case the getNewHttpClient should be public.
Now you can perform unit testing like this:
var mockedClient = SetupMockClient(new HttpResponseMessage
{
StatusCode = HttpStatusCode.InternalServerError,
Content = new StringContent("Failure")
});
var sut = new VerifiableClassA(mockedClient);
sut.Start();
You should consider to use a Factory/Repository-Pattern. They make it easier to Test your methods because you can just inject mocks of objects like HttpClient. The answer Peter Csala suggested is pretty dirty because you create a class that has the single purpose to be used in a test.
Problem:
I am new to writing unit testing in c# using xunit. So I am trying to mock the MongoDB connection. In my project I have use repository pattern there I have used unit of work class like this. so I am accessing every repository via it. so the unit of work class code is here.
namespace QuestionBank.API.Repositories
{
public class UnitOfWork : IUnitOfWork
{
public readonly IQuestionsBankDBContext _context;
private readonly ILogger<UnitOfWork> _logger;
private Dictionary<Type, object> repositories;
private IQuestionsRepository _questionsRepository;
private ICampaignQuestionsRepository _campaignQuestionsRepository;
private ICandidateAnswerRepository _candidateAnswerRepository;
private IIntergrationEventLogRepository _integrationEventLogRepository;
private IControlRepository _controlRepository;
public UnitOfWork(IQuestionsBankDBContext context, ILogger<UnitOfWork> logger)
{
_context = context;
_logger = logger;
}
public IQuestionsRepository QuestionsRepository
{
get
{
this._questionsRepository = new QuestionsRepository(_context as IQuestionsBankDBContext, this, _logger);
return this._questionsRepository;
}
}
public ICandidateAnswerRepository CandidateAnswerRepository
{
get
{
this._candidateAnswerRepository = new CandidateAnswerRepository(_context as IQuestionsBankDBContext, this, _logger);
return this._candidateAnswerRepository;
}
}
public ICampaignQuestionsRepository CampaignQuestionsRepository
{
get
{
this._campaignQuestionsRepository = new CampaignQuestionsRepository(_context as IQuestionsBankDBContext, this, _logger);
return this._campaignQuestionsRepository;
}
}
public IIntergrationEventLogRepository IntegrationEventLogRepository
{
get
{
this._integrationEventLogRepository = new IntergrationEventLogRepository(_context as IQuestionsBankDBContext, this, _logger);
return this._integrationEventLogRepository;
}
}
public IControlRepository ControlRepository
{
get
{
this._controlRepository = new ControlRepository(_context as IQuestionsBankDBContext, this, _logger);
return this._controlRepository;
}
}
public IGenericRepository<TDocument> GetRepository<TDocument>() where TDocument : IDocument
{
if (this.repositories == null)
{
this.repositories = new Dictionary<Type, object>();
}
var type = typeof(TDocument);
if (!this.repositories.ContainsKey(type))
{
this.repositories[type] = new GenericRepository<TDocument>(_context);
}
return (IGenericRepository<TDocument>)this.repositories[type];
}
}
}
So in the unit test to mock services and repositories, I need to pass database context to unitofwork. I tried it this way.
var mockDbContext = new Mock<QuestionsBankDBContext>();
var dbContext = mockDbContext.Object;
var mock = new Mock<ILogger<UnitOfWork>>();
_logger = mock.Object;
unitOfWork = new UnitOfWork(dbContext, _logger);
questionsService = new QuestionsService(unitOfWork);
campaignQuestionsService = new CampaignQuestionsService(unitOfWork);
tokenService = new TokenService();
stringLocalizer = new Mock<IStringLocalizer<SharedResource>>();
questionBankIntergrationEventService = new Mock<IQuestionBankIntergrationEventService>();
questionsController = new QuestionsController(questionsService, campaignQuestionsService, stringLocalizer.Object, tokenService, questionBankIntergrationEventService.Object);
contextMock = new Mock<HttpContext>();
And this is my DB context class.
using MongoDB.Driver;
using QuestionBank.API.Models;
namespace QuestionBank.API.Data
{
public class QuestionsBankDBContext : IQuestionsBankDBContext
{
public IMongoClient Client { get; set; }
public IMongoDatabase Database { get; set; }
public QuestionsBankDBContext(IQuestionBankDatabaseSettings settings)
{
Client = new MongoClient(settings.ConnectionString);
Database = Client.GetDatabase(settings.DatabaseName);
}
}
}
Then I wrote a unit test like this.
[Theory]
[InlineData("61879e54e86be1fa5e41831f")]
[InlineData("61879e54e86be1fa5e41831e")]
public async Task GetQuestionById(string questionId)
{
var actionResult = await questionsController.GetQuestionById(questionId);
var result = actionResult as ObjectResult;
Assert.NotNull(result.Value);
if (result.StatusCode == (int)System.Net.HttpStatusCode.OK)
{
Assert.IsType<Questions>(result.Value);
}
else if (result.StatusCode == (int)System.Net.HttpStatusCode.NotFound)
{
Assert.Contains("ErrorCode", result.Value.ToString());
}
else if (result.StatusCode == (int)System.Net.HttpStatusCode.InternalServerError)
{
var code = (int)ErroCodes.InternalServerError;
Assert.Contains(code.ToString(), result.Value.ToString());
}
}
Then when running this it gives
And my question controller GetQuestionById is like this.
[HttpGet]
//[Authorize(Roles = "SuperAdmin,Admin")]
[Route("getquestionbyidfrombank")]
[ProducesResponseType(typeof(Questions), 200)]
[ProducesResponseType(typeof(string), 404)]
[ProducesResponseType(typeof(string), 500)]
public async Task<IActionResult> GetQuestionById([FromQuery] string questionId)
{
try
{
string errorText;
if (!string.IsNullOrEmpty(questionId))
{
var question = await
questionsService.GetQuestionById(questionId);
return Ok(question);
}
else
{
errorText = string.Format(stringLocalizer[Constants.ErrorCodeString],
(int)ErroCodes.SpecifiedItemNotFound,
stringLocalizer[Helper.ToEnumString(ErroCodes.SpecifiedItemNotFound)]);
return StatusCode(404, errorText);
}
}
catch (Exception ex)
{
string exceptionData =
$"Exception occured while getiing question by id. " +
$"\nException Data: Message- {ex.Message}; " +
$"InnerException- {ex.InnerException}; StackTrace- {ex.StackTrace}";
string errorText = string.Format(stringLocalizer[Constants.ErrorCodeString],
(int)ErroCodes.InternalServerError,
stringLocalizer[Helper.ToEnumString(ErroCodes.InternalServerError)]);
return StatusCode(500, errorText);
}
}
this is how I do the service instantiation.
public QuestionsService(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
Get question by id function
public async Task<Questions> GetQuestionById(string id)
{
var question = await _unitOfWork.QuestionsRepository.FindByIdAsync(id);
return question;
}
Can someone help me to write this unit test correctly and solve this issue. I tried a lot to find out a way to do this but I could not able to do it. Thank you
Don't - just because something can be mocked, doesn't mean it should.
Instead, you can a docker image to run Mongo, drop and create a DB per test class init, drop collections per test init.
Testing a DAL (data-access-layer) without the DB is a waste of time and won't help you actually find bugs.
When you unit test your other components, mock the entire DAL to return the objects you expect.
And do not skip writing a test for your entire service that includes an empty/pre-populated-via-test-config-data DB.
Also, the fields in your DbContext should be private, not public and while passing a constant instance of a DbContext to MongoDB is OK, because MongoDB context is stateless (no transactions and connections are pooled), in general passing a DbContext via constructor is wrong (because relational DBs have transactions and the connections should not be kept open), instead pass a Func<DbContext> (e.g. public MyClass(Func<DbContext> contextConstructor)) which returns the constructor and have DbContext implement IDisposable. This way the client class can do using (context = contextCreator()) { ... }.
You should create a mock of the interface IQuestionsBankDBContext not the class QuestionsBankDBContext. Apart from that your test is not a unit test, it's more of an integration test because you are creating an instance of a controller and some services, and you mock only the database layer of your application. In unit tests you should test only a single layer. If you write unit tests for a controller then you should mock the services and their behavior (the direct dependencies of the controller).
Looking at your controller method, first unit test should check that when you pass not null and not empty question id then your method returns OkResult. For this test the mock of questionsService could return a question object.
Next test should check the opposite case, when it gets empty string it should return a response with code 404. (In my opinion it should be 400 but we are talking about unit tests)
Last test should check the exception handling. For example you pass a valid questionId in the test, but the mock of questionService throws an exception. In this test you can assert that the response has status code 500.
If you want to test many cases with one test method (a Theory in XUnit) then all these cases should have the same result, or you should provide an expected result with each case.
I'm currently trying to create an integration test for my .NET Core Web API.
I'm following the advice here:
https://timdeschryver.dev/blog/how-to-test-your-csharp-web-api
When I run my test to check the HttpStatusCode 'Should' 'Be' ok, the test is failing BUT the test does pass if I use the '/weatherforecast' example that comes with the .NET Core Web API template. If I use my project, the test fails. Is this because I have no values created in the DB when I start the app? How do I create those values? The code for my test is below:
[Fact]
public async Task Get_Should_Retrieve_Score()
{
var response = await Client.GetAsync("/diceroll");
response.StatusCode.Should().Be(HttpStatusCode.OK);
//var DiceRoll = JsonConvert.DeserializeObject<DiceRoll[]>(await response.Content.ReadAsStringAsync());
//forecast.Should().HaveCount(0);
}
Thanks for any help anyone can provide!
You can do a simple test this way:
public class WeatherForecastControllerTests:
IClassFixture<WebApplicationFactory<Api.Startup>>
{
public HttpClient Client { get; }
public
WeatherForecastControllerTests(WebApplicationFactory<Api.Startup>
fixture)
{
Client = fixture.CreateClient();
}
}
public class WeatherForecastControllerTests:
IClassFixture<WebApplicationFactory<Api.Startup>>
{
readonly HttpClient _client { get; }
public
WeatherForecastControllerTests(WebApplicationFactory<Api.Startup>
fixture)
{
_client = fixture.CreateClient();
}
[Fact]
public async Task Get_Should_Retrieve_Forecast()
{
var response = await _client.GetAsync("/weatherforecast");
response.StatusCode.Should().Be(HttpStatusCode.OK);
var forecast = JsonConvert.DeserializeObject<WeatherForecast[]>
(await response.Content.ReadAsStringAsync());
forecast.Should().HaveCount(5);
}
}
Here you have an example how to test it.
https://timdeschryver.dev/blog/how-to-test-your-csharp-web-api
Using C# I'm trying to unit test controller actions and time how long it takes for them to return. I'm using the unit testing framework built into VS2012 Ultimate.
Unfortunately I'm also trying to wrap my head around TestContext and how to use it..
Some example code (my controller action):
[HttpPost]
public JsonResult GetUserListFromWebService()
{
JsonResult jsonResult = new JsonResult();
WebService svc = new WebService();
jsonResult.Data = svc.GetUserList(User.Identity.Name);
return jsonResult;
}
When I try to unit test this, User.Identity.Name is null so it throws an exception. My current unit test code looks like:
[TestClass]
public class ControllerAndRepositoryActionTests {
public TestContext testContext { get; set; }
private static Repository _repository;
private username = "domain\\foobar";
private static bool active = true;
[ClassInitialize]
public static void MyClassInitialize(TestContext testContext)
{
_repository = new WebServiceRepository();
}
#region Controller method tests
[TestMethod]
public void GetUserListReturnsData()
{
Controller controller = new Controller();
var result = controller.GetUserListFromWebService();
Assert.IsNotNull(result.Data);
}
#endregion
#region service repository calls - with timing
[TestMethod]
public void GetUserListTimed()
{
testContext.BeginTimer("Overall");
var results = _repository.GetUserList(username, active);
foreach (var result in results)
{
Console.WriteLine(result.UserID);
Console.WriteLine(result.UserName);
}
testContext.EndTimer("Overall");
}
#endregion
}
Can I use TestContext to set the User.Identity that will be eventually used in the GetUserListFromWebService call?
If I can, what is the accepted way to assign TestContext. When I get it as a param in MyClassInitialize do I set my member variable, or am I supposed to pass it as a param to the TestMethods in some way?
Am I completely missing the point and should I be using some other mocking framework?
To make this test to work, I should change the signature of your class. Because you can not make a stub or a mock of your class Webservice, because you are creating it in the method.
class YourClass
{
private readeonly WebService _ws;
public YourClass(WebService ws)
{
_ws=ws;
}
[HttpPost]
public JsonResult GetUserListFromWebService()
{
JsonResult jsonResult = new JsonResult();
jsonResult.Data = _ws.GetUserList(User.Identity.Name);
return jsonResult;
}
}
Now you can in your test easily mock the class WebService with Moq or other frameworks. To make it eaven easier you shoul create an interface to your class WebService that implements the method GetUserList();
And to mock the User.Identy
public SomeController CreateControllerForUser(string userName)
{
var mock = new Mock<ControllerContext>();
mock.SetupGet(p => p.HttpContext.User.Identity.Name).Returns(userName);
mock.SetupGet(p => p.HttpContext.Request.IsAuthenticated).Returns(true);
var controller = new SomeController();
controller.ControllerContext = mock.Object;
return controller;
}
Or read this blog post http://weblogs.asp.net/rashid/