I am currently writing unit tests for a TagHelper that uses IActionDescriptorCollectionProvider in its constructor. My setupt looks as below so far - any ideas on how to set up something to satisfy IActionDescriptorCollectionProvider and allow me to change the value of IActionDescriptorCollectionProvider.ActionDescriptors.Items for my testing?
I can't use the DefaultActionDescriptorCollectionProvider implementation as it is inaccessible. Is there a simple way of doing this, or should I just substitute it with NSubstitute?
[SetUp]
public void Setup()
{
IHttpContextAccessor contextAccessor = new HttpContextAccessor();
contextAccessor.HttpContext = new DefaultHttpContext();
contextAccessor.HttpContext.Request.Path = "/foo";
IActionDescriptorCollectionProvider actionDescriptorCollectionProvider;
actionDescriptorCollectionProvider = new DefaultActionDescriptorCollectionProvider();
ActivePathSegmentTagHelper helper;
helper = new ActivePathSegmentTagHelper(contextAccessor, actionDescriptorCollectionProvider);
helper.Area = "";
helper.Controller = "Home";
helper.Action = "Index";
}
As far as I know, if you want to use IActionDescriptorCollectionProvider in unit test, you could directly mock it and then use delegate with it.
More details, you could refer to below test demo source codes:
https://github.com/dotnet/aspnetcore/blob/c565386a3ed135560bc2e9017aa54a950b4e35dd/src/Mvc/Mvc.Core/test/Routing/ActionEndpointDataSourceBaseTest.cs
Its too late for u.. but for someone else heading this problem.. I just used this class
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Moq;
using System.Collections.Generic;
public abstract class ActionDescriptorCollectionProviderMock
{
public static readonly IActionDescriptorCollectionProvider ADCP = GetADCP();
private static IActionDescriptorCollectionProvider GetADCP()
{
var actionDescriptorCollectionProviderMock = new Mock<IActionDescriptorCollectionProvider>();
actionDescriptorCollectionProviderMock.Setup(m => m.ActionDescriptors).Returns(new ActionDescriptorCollection(new List<ActionDescriptor>(), 0));
return actionDescriptorCollectionProviderMock.Object;
}
}
and in test:
[Fact]
public async void GetByIdTest()
{
//Arrange
var controller = new SomeController(ActionDescriptorCollectionProviderMock.ADCP);
//Act
...
//Assert
...
}
Related
I have bunch of tests class such as CustomerTest, ProductTest, VendorTest and so on. I'm using in-memory Database and I would like to seed all data that I need to ONLY ONCE before running all these Tests above but have no idea how to do it.
I have CustomWebApplicationFactory class
public class CustomWebApplicationFactory<TProgram> : WebApplicationFactory<Program>
{
public ApplicationContext context { get; set; }
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureTestServices(services => {
var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<ApplicationContext>));
if (descriptor != null)
services.Remove(descriptor);
services.AddDbContext<ApplicationContext>(options => options
.UseInMemoryDatabase("testDB")
);
var serviceProvider = services.BuildServiceProvider();
using (var scope = serviceProvider.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var db = scopedServices.GetRequiredService<ApplicationContext>();
db.Database.EnsureDeleted();
db.Database.EnsureCreated();
SeedData(db); //seeding all data to all table
}
});
}
}
My Tests class
[TestClass]
public class VendorTest : BaseTest
{
[TestMethod]
public async Task AddVendor()
{
var content = JsonSerializer.Serialize(
new
{
name = "VENDORTEST"
}
);
var response = await _httpClient.PostAsync(
"vendors",
new StringContent(content, Encoding.UTF8, "application/json")
);
Assert.IsTrue(response.IsSuccessStatusCode);
}
[TestMethod]
public async Task GetVendor()
{
var response = await _httpClient.GetAsync("vendors");
string result = await response.Content.ReadAsStringAsync();
Assert.IsNotNull(response);
}
}
Base Test
public class BaseTest
{
protected static CustomWebApplicationFactory<Program> _webAppFactory = new CustomWebApplicationFactory<Program>();
protected static HttpClient _httpClient = _webAppFactory.CreateDefaultClient();
[AssemblyInitialize]
public void Initialize()
{
//not sure if BaseClass is the way to do it
//_httpClient = _webAppFactory.CreateDefaultClient();
}
}
Everytime a TestMethod is running, it will reseed all of the data due to CustomWebApplicationFactory. Any idea how to do it only once?
First of all: I dislike your idea so much, that I hesitated to show you a possible approach. I'll explain that after the code.
If you need something, that runs only once, static things come to mind. So, you could use a static/singleton factory to create your in-memory database like this:
public class TestDbFactory
{
private static TestDbFactory instance;
private readonly ApplicationContext applicationContext;
private TestDbFactory()
{
var databaseName = Guid.NewGuid().ToString();
var inMemoryContextOptions = new DbContextOptionsBuilder<ApplicationContext>()
.UseInMemoryDatabase(databaseName)
.Options;
this.applicationContext = new ApplicationContext(inMemoryContextOptions);
this.InitDatabase();
}
public static TestDbFactory Instance => CreateOrReuseInstance();
public ApplicationContext ApplicationContext => this.applicationContext;
private static TestDbFactory CreateOrReuseInstance()
{
if (instance != null) return instance;
var semaphore = new SemaphoreSlim(1, 1);
instance = instance ?? new TestDbFactory();
semaphore.Release(1);
return instance;
}
private void InitDatabase()
{
// ensure deleted & created & seeded for this.applicationContext
}
}
Now you can either use TestDbFactory.Instance.ApplicationContext to pass the database to the systems under test or you can use the factory pattern of the dependency injection setup to provide the context:
services.AddScoped(_ => TestDbFactory.Instance.ApplicationContext);
What I don't like about your idea:
Even for a single test you have to seed the whole database.
You can't do write test, because then tests don't have a predictable setup (it might be or not, that the db contents have changed before a test runs).
To me a test also serves as documentation. This is not possible, if the database content relevant for a specific test can't be determined from the test.
Adding new tests and updating the seeding will become a nightmare as you (let alone a whole team) have to verify the new data vs. all existing tests.
You won't save much typing as you still have to data for every test.
So, to me a better approach would be to use an in-memory database in every test class (a test class contains all tests for a single method). It would be ok to provide a basic seeding common to all tests in the test class, but specific data setup for a test should go with the test.
I hope that this post helps in one way or the other.
I an getting the below issue when running the Unit Test project.
Unable to get Default Constructor For class ********
[TestClass]
public class PersonRegistration
{
private ILoggingService _loggingService;
private IUserManager _userManager;
public PersonRegistration(IUserManager userManager, ILoggingService loggingService)
{
this._userManager = userManager;
this._loggingService = loggingService;
}
[TestMethod]
public void TestMethod1()
{
RegisterBindingModel model = new RegisterBindingModel();
AccountController ac = new AccountController(_userManager, _loggingService);
model.UserName = "test123#gmail.com";
var result = ac.Register(model);
Assert.AreEqual("User Registered Successfully", result);
}
How to fix that. Some answers says that to use a parameter less constructor. But here I need params.
RegisterBindingModel()
public class RegisterBindingModel
{
public RegisterBindingModel();
[Display(Name = "User name")]
[Required]
public string UserName { get; set; }
}
Issue
I've just tested this in my unit tests.
Add
public PersonRegistration()
{
}
And it should run fine.
There is no need for constructors on your unit test classses. If you are using a mocking framework like Moq then I use a factory to return the dependent moqs for the classes I'm testing.
public ILoggingService ReturnMockLoggingService()
{
var mockService = new Mock<ILoggingService>();
return mockService.Object;
}
Then in the test fixture.
[TestMethod]
public void TestMethod1()
{
RegisterBindingModel model = new RegisterBindingModel();
var logService = MockFactory.ReturnMockLoggingService();
var userService = MockFactory.ReturnMockUserService();
AccountController ac = new AccountController(userService, logService);
model.UserName = "test123#gmail.com";
var result = ac.Register(model);
Assert.AreEqual("User Registered Successfully", result);
}
if you're not using mocks then simply instance the user and log service in the test or create a SetUp.
[ClassInitialize]
public void SetUp()
{
_loggingService = new LoggingService();
_userManager = new UserManager();
}
Hope that helps.
You should use a mocking framework like Moq.
Example:
[TestClass]
public class PersonRegistration
{
[TestMethod]
public void TestMethod()
{
RegisterBindingModel model = new RegisterBindingModel();
var mockService = new Mock<ILoggingService>();//Mock
//Do something as per your requirement
//var reg= new List<RegisterBindingModel >(); // provide some sample list
//mockService .Setup(r => r.GetAll=()).Return(reg);
var mockManager = new Mock<IUserManager>();//Mock
//Do something as per your requirement
//var user= new List<User>(); // provide some sample list
//mockManager .Setup(r => r.GetAll=()).Return(user);
AccountController ac = new AccountController(mockManager.Object, mockService.Object);
model.UserName = "test123#gmail.com";
var result = ac.Register(model);
Assert.AreEqual("User Registered Successfully", result);
}
}
You can get help form this and this link.
Totally new to testing, I have a controller like this:
public class CheckRegController : Controller
{
private readonly ApplicationDbContext _context;
private readonly AppSettings _appSettings;
public CheckRegController(ApplicationDbContext context, IOptions<AppSettings> appSettings)
{
_context = context;
_appSettings = appSettings.Value;
}
[HttpGet]
public IActionResult Get(string var1, int numberusers)
{
//...
}
}
Now I have added a test project in which I am trying to use XUnit and Moq. I just wanted to create an object of controller like I have done in a very simple project, but it doesn't work in this.When I tried:
CheckRegController cr = new CheckRegController();
It says:
There is no argument given that corresponds to the required formal
parameter 'context' of
'CheckRegController.CheckRegController(ApplicationDbContext,
IOptions)' XUnitTestProjectOA
Then I tried:
var moqHome = new Mock<ApplicationDbContext>();
but I don't know is it right or not OR what I need to do ahead?
How to pass _context and _appsettings ??
You were on the right track. Mock the dependencies and inject them into the subject under test.
//Arrange
var dbmock = new Mock<ApplicationDbContext>();
//...setup dbmock as needed to exercise test
var options = new Mock<IOption<AppSetting>();
var appSetting = new AppSetting {
//...populate appSetting as needed to exercise test
};
options.Setup(_ => _.Value).Returns(appSetting);
var sut = new CheckRegController(dbMock.Object, options.Object);
var var1 = "testing";
var numberusers = 2;
//Act
var actual = sut.Get(var1,numberusers)
//Assert
//...assert expected behavior to actual.
I would also suggest abstracting away the context to also make the controller more maintainable.
I'm setting up regression testing for my ASP.NET 5 project using beta8. When I setup the test fixtures I want to fire up kestrel so that I could run selenium tests against it without the need for any external web server. How do I do this?
It's basically something like this:
public class RegressionTests : IDisposable
{
public RegressionTests()
{
// Start kestrel
}
[Fact]
public void Test1()
{
Assert.True(true);
// more tests...
}
public void Dispose()
{
// Shutdown kestrel
}
}
This is what I've tried so far but I couldn't get it to work. It doesn't pick up the project.json file. Well, to be honest, I don't know what to pass to it since I can't find anywhere what command args I can pass to Microsoft.AspNet.Hosting.Program.
new Microsoft.AspNet.Hosting.Program(CallContextServiceLocator.Locator.ServiceProvider).Main(
new[]
{
"--server",
"Microsoft.AspNet.Server.Kestrel",
"--project",
"../Web/project.json",
"--port",
"5001",
});
Thanks #Victor Hurdugaci. For the google folks of the future, this is what I ended up having. This is a test fixture that I use for xunit. The TestConfiguration class is missing but you should get the idea. You need to add a dependency on Microsoft.AspNet.Server.Testing.
public class WebTestsFixture : IDisposable
{
private readonly IApplicationDeployer _deployer;
private readonly IDisposable _loggerScope;
public WebTestsFixture()
{
var logger = new LoggerFactory()
.AddConsole(LogLevel.Information)
.CreateLogger("Regression");
_loggerScope = logger.BeginScope("RegressionTestSuite");
var deploymentParameters = new DeploymentParameters(
TestConfiguration.Configuration.Get<string>("Settings:ApplicationPath"),
(ServerType)Enum.Parse(typeof(ServerType), TestConfiguration.Configuration.Get<string>("Settings:ServerType")),
RuntimeFlavor.Clr,
RuntimeArchitecture.x86)
{
ApplicationBaseUriHint = TestConfiguration.Configuration.Get<string>("Settings:ApplicationUri"),
EnvironmentName = TestConfiguration.Configuration.Get<string>("Settings:EnvironmentName"),
PublishWithNoSource = false
};
_deployer = ApplicationDeployerFactory.Create(deploymentParameters, logger);
DeploymentResult = _deployer.Deploy();
}
public DeploymentResult DeploymentResult { get; private set; }
public void Dispose()
{
_loggerScope.Dispose();
_deployer.Dispose();
}
}
#mardoxx points out that a more modern and much simpler approach to testing is documented here.
I have webapi which for testing purposes I am hosting in owin. I have set it up using autofac. now when I am testing I want to inject moq dependencies. which I am not able to so far. I have read the documentation and did bit of research but I am missing something.
here is the testing code.
[Test]
public void Request_all_airports()
{
const int port = 8086;
AirportCollection moqAirportCollection = new AirportCollection();
moqAirportCollection.Airports = new List<Airport>{new Airport{IATA = "moq",Name = "moqName"}};
using (WebApp.Start<Startup>("http://localhost:" + port))
{
using (var mock = AutoMock.GetLoose())
{
var moqObj = mock.Mock<IAirportService>().Setup(x => x.GetAirports()).Returns(moqAirportCollection);
var client = new HttpClient {BaseAddress = new Uri("http://localhost:" + port)};
var response = client.GetAsync("/api/airport/get").Result;
var body = response.Content.ReadAsStringAsync().Result;
var airportCollection = JsonConvert.DeserializeObject<AirportCollection>(body);
}
}
}
Please have a look. let me know what I am missing. if you want to look at controller code or any other piece do let me know .
here is code for startup
public class Startup
{
public static IContainer container { get; set; }
public void Configuration(IAppBuilder appBuilder)
{
var httpConfig = new HttpConfiguration();
container = AutofacSetup.Register(httpConfig);
WebApiConfig.Register(httpConfig);
appBuilder.UseAutofacMiddleware(container);
appBuilder.UseAutofacWebApi(httpConfig);
appBuilder.UseWebApi(httpConfig);
}
}
Thanks
I think so I have solved it with help from people. here is my code.
var moq = new Mock<IAirportService>();
moq.Setup(x => x.GetAirports()).Returns(moqAirportCollection);
newBuilder.RegisterInstance(moq.Object).As<IAirportService>();
newBuilder.Update(Startup.container);
I havnt rebuild the contrain i just updated it. autofac have behavior to use latest registration so it will use mocked on here.
You are almost there.
In your test you need to register your mock service with your autofac container so that dependencies on IAirportService are resolved with the mock in the application.
One way to achieve this is override the Startup class' Configuration method for each test and put your test DI in there. I've put some comments below to show changes that can be made:
public class Startup
{
public static IContainer container { get; set; }
// make this virtual
public virtual void Configuration(IAppBuilder appBuilder)
{
var httpConfig = new HttpConfiguration();
// have this return the ContainerBuilder instead of the container
var builder = AutofacSetup.Register(httpConfig)
container = builder.Build();
WebApiConfig.Register(httpConfig);
appBuilder.UseAutofacMiddleware(container);
appBuilder.UseAutofacWebApi(httpConfig);
appBuilder.UseWebApi(httpConfig);
}
}
Then in your test class, derive from the Startup class and put your test logic in. Something like this:
public class MyTestCase {
public static Mock<IAirportService> MockObj { get; set; }
private class TestStartup : Startup {
public override void Configuration(IAppBuilder app) {
var httpConfig = new HttpConfiguration();
// this now returns ContainerBuilder instead of the container
var builder = AutofacSetup.Register(httpConfig)
// register your mock, change this to whatever lifetime scope you need
var moqAirportCollection = new AirportCollection();
moqAirportCollection.Airports = new List<Airport>{new Airport{IATA = "moq",Name = "moqName"}};
var mock = AutoMock.GetLoose()
MockObj = mock.Mock<IAirportService>()
.Setup(x => x.GetAirports())
.Returns(moqAirportCollection);
var moqObj = MockObj.Object;
builder.RegisterInstance(moqObj).As<IAirportService>();
container = builder.Build();
WebApiConfig.Register(httpConfig);
appBuilder.UseAutofacMiddleware(container);
appBuilder.UseAutofacWebApi(httpConfig);
appBuilder.UseWebApi(httpConfig);
}
}
[Test]
public void Request_all_airports()
{
using (var server = WebApp.Start<Startup>())
{
var response =
server.CreateRequest("/api/airport/get")
.GetAsync()
.Result;
var body = response.Content.ReadAsStringAsync().Result;
var result = JsonConvert.DeserializeObject<AirportCollection>(body);
// assert something
}
}
}
A unit test should test a single component. In your case, you are trying to test the AirportController through a HTTP query, not the AirportController as a standalone component.
The AirportController class depends on a IAirportService component. In order to test the component without any dependency you created a moq on IAirportService. Now you can instantiate a new AirportController with this moq and run your test using this instance.
If you have a AirportController like this
public class AirportController
{
public AirportController(IAirportService airportService) { /* ... */}
}
The AirportController test should be like this :
[Test]
public void Request_all_airports()
{
AirportCollection moqAirportCollection = new AirportCollection();
var moqAirPort = new Airport{ IATA = "moq",Name = "moqName" };
moqAirportCollection.Airports = new List<Airport>{ moqAirPort };
using (var mock = AutoMock.GetLoose())
{
var moqAirportService = mock.Mock<IAirportService>()
.Setup(x => x.GetAirports())
.Returns(moqAirportCollection);
var testedAirportController = new AirportController(moqAirportService);
AirportCollection airportCollection = testedAirportController.Get();
Assert.AreEquals(1, airportCollection.Length, "Invalid number of airport");
Assert.AreEquals(moqAirPort.Name, airportCollection[0].Name, "Invalid name");
}
}