I am building a new app using ASP.NET 5 with MVC6 and trying to set up a project with tests based on this description.
It is working fine for API calls (returning ObjectResults), but when I hit a response returning a view, I get 404 response.
I am using startup class from the project unter test, and the code is the same as in tutorial:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseIISPlatformHandler();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
public static void Main(string[] args) => WebApplication.Run<Startup>(args);
}
[Route("/")]
public class HomeController : Controller
{
[HttpGet]
[AllowAnonymous]
[Route("/")]
public IActionResult Index()
{
return View();
}
[HttpGet]
[AllowAnonymous]
[Route("/ok")]
public IActionResult Ok()
{
return new ObjectResult("OK");
}
}
[TestClass]
public class HomeTests
{
private readonly HttpClient _client;
public HomeTests()
{
var server = new TestServer(TestServer.CreateBuilder()
.UseStartup<Startup>());
_client = server.CreateClient();
}
[TestMethod]
public async Task Index_returns_page()
{
var response = await _client.GetAsync("/");
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
//Assert.Equals("OK", responseString);
}
[TestMethod]
public async Task Index_OK_returns_OK()
{
var response = await _client.GetAsync("/ok");
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
Assert.AreEqual("OK", responseString);
}
}
Of course in the browser everything works as expected.
It can't find your views because the IApplicationEnvironment.ApplicationBasePath is set to the test project's directory. You should be replacing the IApplicationEnvironment implementation with your own that points at the project you're testing. This will get a bit easier in RC2.
Related
I created an an API application using .NET Core on VS Code.
I type dotnet run in the terminal and its result is shown in the below pic:
but when I browse URL in browser it shows:
what should I do? I changed the port but not work yet.
this is my StartUp:
namespace API
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
// services.AddSwaggerGen(c =>
// {
// c.SwaggerDoc("v1", new OpenApiInfo { Title = "API", Version = "v1" });
// });
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
// app.UseSwagger();
// app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "API v1"));
}
// app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
and this is my program.cs:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
If you check the code, the default route of API controller may look like this Route("api/[controller]"), which does not include [action] token in attribute route.
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET /api/values
[HttpGet]
public IActionResult Get()
{
return Ok("Data From Get Action");
}
// GET /api/values/3
[HttpGet("{id}")]
public IActionResult Get(int id)
{
return Ok(id);
}
// POST /api/values
[HttpPost]
public IActionResult Post()
{
return Ok("Data From Post Action");
}
}
To access the first action, you can make request with following URL.
https://localhost:5001/api/values
Besides, if you really want to match /api/values/get to your endpoint, you can try to modify the code as below.
// GET /api/values/get
[HttpGet("get")]
public IActionResult Get()
{
return Ok("Data From Get Action");
}
I've been coding a simple API with the view of getting placeholder image of a specialized site which offers this service. However, when I make a request to site with correspondent path, I'm not able of getting the image displayed on screen instead I got this HTML:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to target URL: http://fakeimg.pl/300x300/. If not click the link.
I read the official documentation all day long but up to now I haven't realized a way to get this work properly.
This is my code. Obs: I have used the url "https://fakeimg.pl/300X300" and the dotnet core version 3.1.302 for this sample request.
FakePhotoService.cs
namespace FakePhotoApi
{
public class FakePhotoService
{
private readonly HttpClient _httpClient;
private readonly ILogger<FakePhotoService> _logger;
public FakePhotoService(HttpClient httpClient, ILogger<FakePhotoService> logger)
{
_logger = logger;
_httpClient = httpClient;
}
public HttpRequestMessage GenerateRequest(Uri uri)
{
return new HttpRequestMessage(HttpMethod.Get, uri);
}
public async Task<string> GetFakePhoto(Tuple<int, int> dimensions)
{
var baseUri = new Uri($"https://fakeimg.pl/{dimensions.Item1}x{dimensions.Item2}");
var request = GenerateRequest(baseUri);
var response = await _httpClient.SendAsync(request);
return await response.Content.ReadAsStringAsync();
}
}
}
FakePhotoController.cs
namespace FakePhotoApi.Controllers
{
[Route("[controller]")]
[ApiController]
public class FakePhotoController : ControllerBase
{
private readonly FakePhotoService _fakePhotoService;
public FakePhotoController(FakePhotoService fakePhotoService)
{
_fakePhotoService = fakePhotoService;
}
[HttpGet("/")]
public async Task<IActionResult> GetFakePhoto()
{
var result = await _fakePhotoService.GetFakePhoto(new Tuple<int, int>(300, 300));
return Ok(result);
}
}
}
Startup.cs
namespace FakePhotoApi
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddHttpClient<FakePhotoService>()
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler
{
AllowAutoRedirect = true,
MaxAutomaticRedirections = 5
};
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
Program.cs
namespace FakePhotoApi
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
Please let me know what I'm doing wrong.
Update your code like so:
public HttpRequestMessage GenerateRequest(Uri uri)
{
var msg = new HttpRequestMessage(HttpMethod.Get, uri);
msg.Headers.Add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36"); // or some other real browser string
return msg;
}
My swagger is not working in my ASP.net core project. It works when I comment out either the put or post action method. I have also tried putting the name of eachpoint to fix the problem but it didnt work. Can anyone tell me how to overcome this issue? Thanks
[HttpPut("{ids}", Name = nameof(Edit))]
[Route("api/[controller]/[action]")]
[ApiController]
public class CompanyController : ControllerBase
{
private readonly IMediator _mediator;
public CompanyController(IMediator mediator)
{
_mediator = mediator;
}
[HttpGet()]
public async Task<ActionResult<List<Company>>> List()
{
return await _mediator.Send(new List.Query());
}
[HttpPost]
public async Task<ActionResult<Unit>> Create(Create.Command command)
{
return await _mediator.Send(command);
}
[HttpGet("{id}")]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
public async Task<ActionResult<Company>> Details(int id)
{
return await _mediator.Send(new Details.Query { Id = id });
}
[HttpPut("{ids}", Name = nameof(Edit))]
public async Task<ActionResult<Unit>> Edit(int ids, Edit.Command command)
{
command.Id = ids;
return await _mediator.Send(command);
}
}
}
This is how I configured swagger in my startup class.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(opt =>
{
opt.AddPolicy("CorsPolicy", policy =>
{
policy.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials()
.WithOrigins("http://localhost:3000");
});
});
// Register the Swagger generator, defining 1 or more Swagger documents
**services.AddSwaggerGen();**
services.AddControllers()
.AddFluentValidation(cfg =>
{
cfg.RegisterValidatorsFromAssemblyContaining<Create>();
});
services.AddMediatR(typeof(List.Handler).Assembly);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors("CorsPolicy");
// Enable middleware to serve generated Swagger as a JSON endpoint.
**app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
// specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});**
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
When trying to test my application i get "This localhost page can’t be found" using vs2017.
Trying to reach https://localhost:44347/test/test
This is my Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseMvc();
}
}
Based upon the code shared by you, you would need to explicitly provide the routes using attribute routing.
public class ProjectenEnPersoneelCONTROLLER : Controller
{
[Route("Test/Test")]
public IActionResult Index()
{
var webClient = new WebClient();
var json = webClient.DownloadString(#"D:\Users\tijnv\source\repos\API_STA_1\API_STA_1\Json\test-request.json");
var projects = JsonConvert.DeserializeObject<Projects>(json);
return View(projects);
}
}
Alternatively you can rename controller to TestController and Action method to Test
public class TestController : Controller
{
public IActionResult Test()
{
var webClient = new WebClient();
var json = webClient.DownloadString(#"D:\Users\tijnv\source\repos\API_STA_1\API_STA_1\Json\test-request.json");
var projects = JsonConvert.DeserializeObject<Projects>(json);
return View(projects);
}
}
Using xUnit 2.4.1 to test the Api always fails to find Controller
When I create a WebApplicationFactory and pass Startup file as parameter the HTTP Client from WebApplicationFactory.CreatVlient() always returns 404 for Get requests.
Testing a .Net Core Api that uses MVC.
The CommonContext is an internal class that sets the connection.
The Configfile reads correctly
The Connectionstring to DB is correct
The Endpoint is not called correctly and therefore never hits the controller.
Class that inherits WebApplocationFactory
public class WebApiTestFactory<TStartup>
: WebApplicationFactory<TStartup> where TStartup: class
{
protected override IWebHostBuilder CreateWebHostBuilder()
{
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables();
var configValues = new Dictionary<string, string>
{
{"RunStatus", "Test"},
};
builder.AddInMemoryCollection(configValues);
return WebHost.CreateDefaultBuilder()
.UseConfiguration(builder.Build())
.UseUrls("http://localhost:53976/")
.UseSetting("applicationUrl", "http://localhost:53976/")
.UseStartup<Else.WebApi.Core.Startup>();
}
}
Unit Test
public class TestControllerTest : IClassFixture<WebApiTestFactory<Startup>>
{
private readonly WebApiTestFactory<Startup> _factory;
public TestControllerTest(WebApiTestFactory<Startup> factory)
{
_factory = factory;
}
[Theory]
[InlineData("api/Test/GetExample")]
public async Task Create(string url)
{
// Arrange
var clientOptions = new WebApplicationFactoryClientOptions();
clientOptions.BaseAddress = new Uri("http://localhost:53976");
var client = _factory.CreateClient(clientOptions);
// Act
var response = await client.GetAsync(url);
// Assert
response.EnsureSuccessStatusCode(); // Status Code 200-299
Assert.Equal("text/html; charset=utf-8",
response.Content.Headers.ContentType.ToString());
}
}
Controller is in the project im testing
[ApiController]
[Route("api/Test")]
public class TestController : Controller
{
[HttpGet("GetExample")]
public ActionResult GetExample()
{
return Ok();
}
}
Startup
public class Startup
{
public Startup(IConfiguration configuration, IHostingEnvironment env)
{
HostingEnvironment = env;
Configuration = configuration;
EwBootstrapper.BootstrapElsewareServices();
}
public IConfiguration Configuration { get; }
public IHostingEnvironment HostingEnvironment { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
if (Configuration["RunStatus"] != "Test")
{
services.AddTransient<AuthenticationTokens>();
services.AddTransient<IPasswordValidator, PasswordValidator>();
services.AddTransient<IUserRepository, UserRepository>();
services.AddMvc();
services.AddScoped(_ =>
new CommonContext(Configuration.GetConnectionString("DbConnection")));
services.AddSwaggerDocumentation();
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) // Configure authentication (JWT bearer)
.AddJwtBearer(jwtOpt => // Configure JWT bearer
{
jwtOpt.TokenValidationParameters = AuthenticationTokens.GetValidationParameters();
});
}
else
{
//services.AddMvcCore().AddJsonFormatters();
services.AddScoped(_ =>
new CommonContext(Configuration.GetConnectionString("DbTestConnection")));
}
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app)
{
if (Configuration["RunStatus"] != "Test")
{
app.UseSwaggerDocumentation();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "API");
});
app.UseMiddleware<ApiLoggerMiddleware>();
app.UseMvc(builder => builder.MapRoute("Default", "api/{controller}/{action=Get}/{id?}")); // No default values for controller or action
app.UseDefaultFiles(); // Enable default documents ( "/" => "/index.html")
app.UseStaticFiles(); // Static files under wwwroot
app.UseAuthentication();
}
if (HostingEnvironment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
}
}
According to the attribute routing in your controller, the action method has the url api/Test/GetExample: [HttpGet("GetExample")], yet in your in test you are testing for CreateExample:
[InlineData("api/Test/CreateExample")]
So I guess, your test is correct in returning a 404. That route simply will not resolve to any existing action method.
I suggest you change your theory to [InlineData("api/Test/GetExample")]