I am new at implementing unit tests and integration tests. I am trying to write some integration tests for my application.
Following are the code snippets from my application to give you all the idea of my code.
It would be great help if you could provide me some guidance for it.
namespace MyApplication.ApiControllers
{
[Authorize]
[RoutePrefix("api/customers")]
[AppExceptionFilter]
public class CustomersController : ApiController
{
private readonly IMediator _mediator;
public CustomersController(IMediator mediator)
{
_mediator = mediator;
}
[HttpGet]
[Route("GetCustomer")]
public async Task<IHttpActionResult> GetCustomer(string customerNumber, string customerType = null)
{
var result = await _mediator.RequestAsync(new GetCustomerRequest(customerNumber, customerType));
return Ok(result);
}
}
}
Following is the implementation for GetCustomerRequest handler
public async Task<List<Customer>> HandleAsync(GetCustomerRequest request)
{
var result = await customerService.GetCustomer(request.CustomerNumber, request.CustomerType);
// some business logic
return result;
}
Following is the implementation for customerService
public async Task<List<Customer>> GetCustomer(string customerNumber, string customerType = null)
{
using (var dataContext = _dataContextFactory.Invoke())
{
result = await dataContext.Customers
.Where(b => b.CustomerNumber == customerNumber)
.Where(b => b.CustomerType == customerType)
.Select(b => new Customer
{
// Properties assignment...
})
.ToListAsync();
}
return result;
}
Below is the integration unit test what I have tried.
namespace MyApplication.Tests.Integrations
{
[TestFixture]
public class CustomersControllerTests
{
private string _baseAddress;
private string _username;
private string _password;
private IApiClient _apiClient;
[SetUp]
public void Setup()
{
_baseAddress = "https://mywebaaplication.com"; // TODO get this from a config
_username = "";
_password = "";
_apiClient = new ApiClient(new ApiClientAuthenticationHandler(), _baseAddress); // REPLACE with AzureADApiClientAuthenticationHandler
}
[Test]
public async Task CustomersController_GetCustomer()
{
var customerNumber = string.Empty;
var customerType = 500;
var result = await _apiClient.GetAsync<Customer[]>($"/api/customers/GetCustomer?customerNumber={customerNumber}&customerType={customerType}");
Assert.IsNotNull(result);
Assert.IsTrue(result?.Length > 0);
}
}
}
You can do a few things:
Create a webhost within your unit test, then do http requests against it
Not test your controller in a unit test, but in a liveness/readiness check (because it's just glue code anyway). Just do integration testing for your service.
Just test against "new CustomersController"
There isn't a right/wrong answer here. You just look at the risks, and test accordingly. Also depends on the type of code-changes you expect. Sometimes its fine to create the test only within the context of a new change, no need to anticipate everything.
Related
I am new to ReactiveUI and trying to test a view model that looks like this:
public interface IService
{
Task<SessionModel> GetData(string id);
}
/// Provides a group of schedulers available to be used
public interface ISchedulers
{
IScheduler Default { get; }
IScheduler Dispatcher { get; }
}
public class MyVm : ReactiveObject
{
IService service;
public MyVm(ISchedulers schedulers, IService service)
{
this.service = service;
this.session = this.WhenAnyValue(x => x.SessionId)
.SelectMany(SearchSession)
.ObserveOn(schedulers.Default)
.ToProperty(this, x => x.Session);
}
private async Task<SessionModel> SearchSession(string id)
{
return await this.service.GetData(id);
}
private string sessionId;
public string SessionId
{
get => sessionId;
set => this.RaiseAndSetIfChanged(ref sessionId, value);
}
readonly ObservableAsPropertyHelper<SessionModel> session;
public SessionModel Session
{
get { return session.Value; }
}
}
public class SessionModel { }
I'm mocking the service call to return dummy data, but not sure what I need to do with a TestScheduler in order to get the SelectMany to work.
Here's a test class that shows how i would create a test for the view model. The goal is to eventually be able to check that the model got set:
[TestClass]
public class MyVmTests
{
[TestMethod]
public void CreateClass
{
var subject = new MyVm(/*pass in mocks*/);
subject.SessionId="test";
Assert.IsNotNull(subject.Session);
}
}
I don't think using TestScheduler is necessary. The following passes for me (using Moq):
var mockSchedulers = new Mock<ISchedulers>();
mockSchedulers.Setup(s => s.Default).Returns(Scheduler.Immediate);
var id = "123";
var mockService = new Mock<IService>();
var returnSession = new SessionModel();
mockService.Setup(s => s.GetData(It.Is<string>(i => i == id)))
.ReturnsAsync(returnSession);
var target = new MyVm(mockSchedulers.Object, mockService.Object);
target.SessionId = id;
Assert.IsNotNull(target.Session);
Assert.AreEqual(returnSession, target.Session);
TestScheduler is best when you're trying to test something with time (like a Delay, proving that the Delay actually happened). You're not really doing that here.
I am trying to mock the OData service context using Moq to return a list of dummy entities so that I could base my unit test on that. I cannot expose my real model and application so I have created this simulated app and the portion, which I have exposed is similar.
MyOdataApplication consuming ODataEndpoint which I am testing.
public class MyApplication
{
private readonly IODataContext _odataContext;
public MyApplication(IODataContext odataContext){
_odataContext = odataContext;
}
public async Task<IEnumerable<Book>> GetBooks(string authorName)
{
IEnumerable<Book> books = null;
var query = (DataServiceQuery<Book>)_odataContext.Books.Where(x => x.Author = authorName);
books = await query.ExecuteAsync().ToList();
return books;
}
public bool async ValidateBooks(string authorName){
var books = await GetBooks(authorname);
//other code....
}
}
My Odata Service contract interface is
public interface IODataContext
{
global::Microsoft.OData.Client.DataServiceQuery<global::models.Book> Books { get; }
}
My Unit Test class is as follows.
[TestFixture]
public class MyTestClass
{
[Test]
public void TestOdataFunctionality()
{
var mockODataEndpoint = new Mock<IODataContext>();
//It fails here as its not able to convery IQueryable<Book> to DataServiceQuery<Book>
mockODataEndpoint.Setup(x => x.GetBooks(It.IsAny<string>)).Returns(GetDummyBooks());
var myApp = new MyApplication(mockODataEndpoint.Object);
//This is my main method which I need to test.
Task<bool> task = myApp.ValidateBooks("author name");
var isvalid = task.Result;
Assert.AreEqual(true, isvalid);
}
private DataServiceQuery<Book>GetDummyBooks()
{
var books = new List<Book>
{
new Book()
{
Name = "Book1",
Author = "author name",
//other properties...
}
};
//Not sure how to achieve this. The below line is giving error ???
return (DataServiceQuery<Book>)books.AsQueryable();
}
}
How do I mock the Odata Service endpoint so that I could test my ValidateBooks method?
I have build a WebAPI and apart from my tests running on Postman I would like to implement some Integration/Unit tests.
Now my business logic is very thin, most of the time its more of CRUD actions, therefore I wanted to start with testing my Controllers.
I have a basic setup. Repository pattern (interfaces), Services (business logic) and Controllers.
The flow goes Controller (DI Service) -> Service (DI Repo) -> Repo Action!
So what I did was override my Startup file to change into a in memory database and the rest should be fine (I would assume) Services are added, repos are added and now I am pointing into a in memory DB which is fine for my basic testing.
namespace API.UnitTests
{
public class TestStartup : Startup
{
public TestStartup(IHostingEnvironment env)
: base(env)
{
}
public void ConfigureTestServices(IServiceCollection services)
{
base.ConfigureServices(services);
//services.Replace<IService, IMockedService>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
base.Configure(app, env, loggerFactory);
}
public override void SetUpDataBase(IServiceCollection services)
{
var connectionStringBuilder = new SqliteConnectionStringBuilder { DataSource = ":memory:" };
var connectionString = connectionStringBuilder.ToString();
var connection = new SqliteConnection(connectionString);
services
.AddEntityFrameworkSqlite()
.AddDbContext<ApplicationDbContext>(
options => options.UseSqlite(connection)
);
}
}
}
I wrote my first test, but the DatasourceService is not there:
The following constructor parameters did not have matching fixture data: DatasourceService datasourceService
namespace API.UnitTests
{
public class DatasourceControllerTest
{
private readonly DatasourceService _datasourceService;
public DatasourceControllerTest(DatasourceService datasourceService)
{
_datasourceService = datasourceService;
}
[Xunit.Theory,
InlineData(1)]
public void GetAll(int companyFk) {
Assert.NotEmpty(_datasourceService.GetAll(companyFk));
}
}
}
What am I missing?
You can't use dependency injection on test classes. You can only let xunit inject special fixtures via constructor (see docs).
For Integration Testing you want to use the TestServer class from Microsoft.AspNetCore.TestHost package and a separate Startup.cs class (easier to setup configuration than inheritance imho).
public class TestStartup : Startup
{
public TestStartup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
public void ConfigureTestServices(IServiceCollection services)
{
services.Replace(ServiceDescriptor.Scoped<IService, MockedService>());
services.AddEntityFrameworkSqlite()
.AddDbContext<ApplicationDbContext>(
options => options.UseSqlite(connection)
);
}
public void Configure(IApplicationBuilder app)
{
// your usual registrations there
}
}
In your unit test project, you need to create an instance of the TestServer and perform the test.
public class DatasourceControllerTest
{
private readonly TestServer _server;
private readonly HttpClient _client;
public DatasourceControllerTest()
{
// Arrange
_server = new TestServer(new WebHostBuilder()
.UseStartup<TestStartup>());
_client = _server.CreateClient();
}
[Xunit.Theory,
InlineData(1)]
public async Task GetAll(int companyFk) {
// Act
var response = await _client.GetAsync($"/api/datasource/{companyFk}");
// expected result from rest service
var expected = #"[{""data"":""value1"", ""data2"":""value2""}]";
// Assert
// This makes sure, you return a success http code back in case of 4xx status codes
// or exceptions (5xx codes) it throws an exception
response.EnsureSuccessStatusCode();
var resultString = await response.Content.ReadAsStringAsync();
Assert.Equals(resultString, expectedString);
}
}
Now, when you call operations which write to the database, you can also check if the data is really written to the database:
[Xunit.Theory,
InlineData(1)]
public async Task GetAll(int companyFk) {
// Act
var response = await _client.DeleteAsync($"/api/datasource/{companyFk}");
// expected result from rest service
// Assert
response.EnsureSuccessStatusCode();
// now check if its really gone in the database. For this you need an instance
// of the in memory Sqlite DB. TestServer has a property Host, which is an IWebHost
// and it has a property Services which is the IoC container
var provider = _server.Host.Services;
var dbContext = provider.GetRequiredService<ApplicationDbContext>();
var result = await dbContext.YourTable.Where(entity => entity.Id == companyFk).Any();
// if it was deleted, the query should result in false
Assert.False(result);
}
Now you can use Xunit.DependencyInjection in your tests.
namespace Your.Test.Project
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IDependency, DependencyClass>();
}
}
}
your DI-classes:
public interface IDependency
{
int Value { get; }
}
internal class DependencyClass : IDependency
{
public int Value => 1;
}
and XUnit-test:
public class MyAwesomeTests
{
private readonly IDependency _d;
public MyAwesomeTests(IDependency d) => _d = d;
[Fact]
public void AssertThatWeDoStuff()
{
Assert.Equal(1, _d.Value);
}
}
I need to unit testing this GetData method.
public MessageResponse GetData(XmlElement requestElement)
{
MessageResponse MsgResponse = new MessageResponse();
if (requestElement.Attributes["employeeNo"] == null){
MsgResponse.Messages = new List<string>();
MsgResponse.Messages.Add("Attribute employeeNo is missing");
MsgResponse.Error = true;
return MsgResponse;
}
if (requestElement.Attributes["xmlEmployeeName"] == null){
MsgResponse.Messages.Add("Attribute xmlEmployeeName is missing");
MsgResponse.Error = true;
return MsgResponse;
}
return MsgResponse;
}
this method needs a XmlElement parameter. how do I mock it? in my code, I first created a xmlDocument, then load the xml file.
XmlDocument doc = new XmlDocument();
doc.Load(xmlFilePath);
requestElement = doc.DocumentElement;
for me to test it, first i need to create a xml file without employeeNo, the create antoher one without name, maybe alot more for other scenarios. it just seems like alot work. is there a better way to test it?
should I use moq or other testing framework to simplify the testing?
You can just create the element you want to test with, w/o reading a file at all:
var doc = new XmlDocument();
doc.LoadXml("<MyTestElement/>");
var myTestElement = doc.DocumentElement;
myTestElement.Attributes["employeeNo"] = "fakeId";
var response = myTestResponder.GetData(myTestElement);
//assert whatever you need to
NOTE: every time you find out that the test is too hard to write, usually this means that your class/method does too much.
I would assume, that your method verifies the input, than does something with the data provided. I would suggest that you abstract the data reading part (using some xml deserializer) to populate the data model you need for your application.
Then run validation on the result of the deserialized data. Something like:
public MessageResponse GetData(XmlElement requestElement)
{
var data = _xmlDeserializer.Deserialize(requestElement);
var validationResult = _validator.Validate(data);
if (validationResult.Errors.Count > 0)
{
//populate errors
return result;
}
_dataProcessor.DoSomethingWithData(data);
}
Take a look at FluentValidation for a nice validation library.
If you go the above route, then your tests will be much simpler.
[TestMethod]
public void GetData_Returns_Correct_Message_When_EmployeeNo_Is_Null()
{
var inputWithoutEmployeeNo = GetElement(#"<input></input>");
var actual = GetData(inputWithoutEmployeeNo);
Assert.IsTrue(actual.Error, "Error should be true when employee no. is missing");
Assert.IsNotNull(actual.Messages);
Assert.AreEqual(1, actual.Messages.Count);
Assert.AreEqual("Attribute employeeNo is missing", actual.Messages[0]);
}
private XmlElement GetElement(string xml)
{
var doc = new XmlDocument();
doc.LoadXml(xml);
return doc.DocumentElement;
}
While working on the unit test, I found out that the code throws a NullReferenceException.
The following unit test demonstrates the issue:
[TestMethod]
[ExpectedException(typeof(NullReferenceException))]
public void GetData_Throws_NullReferenceException_When_EmployeeNo_Is_Not_Null_And_XmlEmployeeName_Is_Null()
{
var inputWithoutEmployeeNo = GetElement(#"<input employeeNo='123'></input>");
GetData(inputWithoutEmployeeNo);
}
Using Moq
using System;
using System.Xml;
using Moq;
using NUnit.Framework;
namespace MockXmlTest
{
[TestFixture]
public class MyServiceTests
{
private MockSetup _mockSetup;
[SetUp]
public void Init()
{
_mockSetup = MockSetup.HappySetup();
}
[Test]
public void MyService_Should_Return_Guid()
{
//Arrange
var myService = _mockSetup.MyService.Object;
var id = 42;
var expected = Guid.Empty.ToString();
//Act
var actual = myService.GetXml(id);
//Assert
Assert.AreEqual(expected, actual.FirstChild.InnerText);
}
}
public class MyService : IMyService
{
public XmlDocument GetXml(int id)
{
var doc = new XmlDocument();
//Do real stuff
return doc;
}
}
public interface IMyService
{
XmlDocument GetXml(int id);
}
public class MockSetup
{
public Mock<IMyService> MyService { get; set; }
public MockSetup()
{
MyService = new Mock<IMyService>();
}
public static MockSetup HappySetup()
{
var mockSetup = new MockSetup();
var mockDoc = CreateMockDoc();
//Matches any id of an integer, returns a XmlDocument mock
mockSetup.MyService.Setup(m => m.GetXml(It.IsAny<int>())).Returns(mockDoc);
return mockSetup;
}
private static XmlDocument CreateMockDoc()
{
//<Main><MyGuid>00000000-0000-0000-0000-000000000000</MyGuid></Main>
XmlDocument mockDoc = new XmlDocument();
XmlElement el = (XmlElement)mockDoc.AppendChild(mockDoc.CreateElement("Main"));
el.AppendChild(mockDoc.CreateElement("MyGuid")).InnerText = It.IsAny<Guid>().ToString();
return mockDoc;
}
}
}
Below is a class (Class1) that I want to test, but I'm not fully satisfied with my Unit Test. Please see below code samples.
System Under Test
public interface IRepository {
string GetParameter(int id);
}
public class Repository {
public string GetParameter(int id) {
return "foo";
}
}
public class ErrorInfo {
public string ErrorCodes { get; set; }
}
public interface IErrorProvider {
ErrorInfo BuildErrorMessage(string errorCodes);
}
public class ErrorProvider {
public ErrorInfo BuildErrorMessage(string errorCodes) {
return new ErrorInfo(){ErrorCodes = errorCodes};
}
}
public class Class1 {
private readonly IRepository _repository;
private readonly IErrorProvider _errorProvider;
public Class1(IRepository repository, IErrorProvider errorProvider) {
_repository = repository;
_errorProvider = errorProvider;
}
public List<ErrorInfo> GetErrorList(int id) {
var errorList = new List<ErrorInfo>();
string paramName = _repository.GetParameter(id);
if (string.IsNullOrEmpty(paramName)) {
string errorCodes = string.Format("{0}, {1}", 200, 201);
var error = _errorProvider.BuildErrorMessage(errorCodes);
errorList.Add(error);
}
return errorList;
}
}
Unit Tests
Below test passes and we check whether the correct error codes being used within the system under test.
[TestMethod]
public void GetErrorList_WhenParameterIsEmpty_ReturnsExpectedErrorCodes2() {
//Arrange
var stubRepo = new Mock<IRepository>();
stubRepo.Setup(x => x.GetParameter(It.IsAny<int>())).Returns(string.Empty);
var stubErrorMock = new Mock<IErrorProvider>();
const int id = 5;
var sut = new Class1(stubRepo.Object, stubErrorMock.Object);
//Act
var result = sut.GetErrorList(id);
//Verify
string verifiableErrorCodes = "200, 201";
stubErrorMock.Verify(x => x.BuildErrorMessage(verifiableErrorCodes));
}
However I would prefer testing the end result. For example, I want to Assert against the error codes that have been used in the production code. Below test fails but I like to know your thoughts on how to Assert against the errorCodes that has been used in the system under test.
[TestMethod]
public void GetErrorList_WhenParameterIsEmpty_ReturnsExpectedErrorCodes1() {
//Arrange
var stubRepo = new Mock<IRepository>();
stubRepo.Setup(x => x.GetParameter(It.IsAny<int>())).Returns(string.Empty);
string expectedErrorCodes = "200, 201";
var stubErrorRepo = new Mock<IErrorProvider>();
stubErrorRepo.Setup(e => e.BuildErrorMessage(It.IsAny<string>()));
const int id = 5;
var sut = new Class1(stubRepo.Object, stubErrorRepo.Object);
//Act
var result = sut.GetErrorList(id);
//Assert
Assert.AreEqual(expectedErrorCodes, result.Single().ErrorCodes);
}
What would be the correct way to test this error codes that has been used in the system?
I suggest to mock only the IRepository and use a real IErrorProvider. Then you can call GetErrorList(id) and check the result.
There is not really right or wrong answer and we have decided to use the Assert test as it test the end result.
I took the TDD approach and re-implemented/analysed as below.
Start with a failing test (to simplify the code I removed the Repository from both test and the sut)
//AssertTest
[TestMethod]
public void GetErrorList_WhenParameterIsEmpty_ReturnsExpectedErrorCodes1()
{
//Arrange
const string expectedErrorCodes = "200, 201";
var stubErrorRepo = new Mock<IErrorProvider>();
stubErrorRepo.Setup(e => e.BuildErrorMessage(expectedErrorCodes)).Returns(new ErrorInfo() { ErrorCodes = expectedErrorCodes });
var sut = new Class1(stubErrorRepo.Object);
//Act
var result = sut.GetErrorList();
//Assert
Assert.AreEqual(expectedErrorCodes, result.Single().ErrorCodes);
}
//SUT
public IEnumerable<ErrorInfo> GetErrorList(int id)
{
yield return new ErrorInfo();
}
As you would expect the test fail.
Now if write enough production code to make this test pass.
public IEnumerable<ErrorInfo> GetErrorList()
{
yield return _errorProvider.BuildErrorMessage("200, 201");
}
The VerifyTest would still fail for the above SUT.
//VerifyTest
[TestMethod]
public void GetErrorList_WhenParameterIsEmpty_ReturnsExpectedErrorCodes2()
{
//Arrange
var stubErrorMock = new Mock<IErrorProvider>();
var sut = new Class1(stubErrorMock.Object);
//Act
sut.GetErrorList();
//Verify
string verifiableErrorCodes = "200, 201";
stubErrorMock.Verify(x => x.BuildErrorMessage(verifiableErrorCodes));
}
However if I want this test to pass, I can write the below production code as below
public IEnumerable<ErrorInfo> GetErrorList()
{
_errorProvider.BuildErrorMessage("200, 201");
return null;
}
Now the VerifyTest passes, but the AssertTest fails.
Both tests are valid in their own ways. However they test different semantics.
AssertTest test whether the end result contains the correct error codes. Verify test ensures
the method is called with the correct error codes. It is important to note that
the end value of the Assert test is based on the setup method "match" provided by the Moq
framework. In other words the setup dictates what the end result would be.
AssertTest would fail if the setup is configured incorrectly or the production code uses error codes that does not match the setup configuration.
It is preferred to use the AssertTest as it test the end result.