I want to create a unit test method for the below method which is receiving a file upload "text file" data and parsing it, I tried to use Moq and created a method but I am still very confused in the concept, I need a sample code, I've read many stackover flow questions but it is all for Controllers not Web API
the method used
// Enable both Get and Post so that our jquery call can send data, and get a status
[HttpGet]
[HttpPost]
public HttpResponseMessage Upload()
{
// Get a reference to the file that our jQuery sent. Even if multiple files, they will
// all be their own request and be the 0 index
if (HttpContext.Current.Request.Files.Count>0)
{
HttpPostedFile file = HttpContext.Current.Request.Files[0];
if (file != null && file.ContentLength > 0)
{
try
{
var extension = Path.GetExtension(file.FileName);
if (!IsFileFormatSupported(extension))
{
var objectSerialized = SerializeData(GetError( GlobalResources.NotSupportedFileExtension));
return BadResponse(objectSerialized);
}
var path = SaveFileGetPath(file);
var result = GetPaySlips(path);
var SerializedData = SerializeData(result);
return OkResponse(SerializedData);
}
catch (System.Exception exception)
{
var SerializedData = SerializeData(GetError( GlobalResources.CouldNotReadFile + " " + exception.Message));
return BadResponse(SerializedData);
}
}
else
{
var SerializedData = SerializeData(GetError(file.FileName + " " + GlobalResources.FileisCorrupt));
return BadResponse(SerializedData);
}
}else
{
var SerializedData = SerializeData(GetError( GlobalResources.FileisCorrupt));
return BadResponse(SerializedData);
}
}
the code I did so far
var FileUploadCtrl = new UploadController();
Mock<HttpRequestMessage> cc = new Mock<HttpRequestMessage>();
UTF8Encoding enc = new UTF8Encoding();
// Mock<HttpPostedFileBase> file1 = new Mock<HttpPostedFileBase>();
//file1.Expect(f=>f.InputStream).Returns(file1.Object.InputStream);
//cc.Expect(ctx => ctx.Content).Returns(new retur);
// cc.Expect(ctx => ctx.Content).Returns();
var content = new ByteArrayContent( /* bytes in the file */ );
content.Headers.Add("Content-Disposition", "form-data");
var controllerContext = new HttpControllerContext
{
Request = new HttpRequestMessage
{
Content = new MultipartContent { content }
}
};
//file1.Expect(d => d.FileName).Returns("FileTest.csv");
//file1.Expect(d => d.InputStream).Returns(new HttpResponseMessage(HttpStatusCode.OK)));
var config = new HttpConfiguration();
//var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/upload");
var route = config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}");
var routeData = new HttpRouteData(route, new HttpRouteValueDictionary { { "controller", "FileUpload" } });
FileUploadCtrl.ControllerContext = new HttpControllerContext(config, routeData, cc.Object);
var r = FileUploadCtrl.Upload();
Assert.IsInstanceOfType(r, typeof(HttpResponseMessage));
Related
I am trying to upload an image to cloudinary cloud. The file converts fine to memory stream but when I try to call upload method of cloudinary to upload the image, I get InvlalidOperationException. What I think is, there is something wrong with converting file to stream.See the image showing error
[HttpPost]
public async Task<IActionResult> AddPhotoForUser(int userId, [FromForm] AddPhotoDto addPhotoDto)
{
try
{
if (userId != int.Parse(User.FindFirst(ClaimTypes.NameIdentifier).Value))
{
return Unauthorized();
}
var userFromRepo = await _datingRepository.GetUser(userId);
var file = addPhotoDto.File;
var uploadResult = new ImageUploadResult();
if (file.Length > 0)
{
using (var stream = file.OpenReadStream())
{
var uploadParams = new ImageUploadParams()
{
File = new FileDescription(file.Name, stream),
Transformation = new Transformation()
.Width(500).Height(500).Crop("fill").Gravity("face")
};
uploadResult = _cloudinary.Upload(uploadParams);
}
}
addPhotoDto.Url = uploadResult.Url.ToString();
addPhotoDto.PublicId = uploadResult.PublicId;
var photo = _mapper.Map<Photo>(addPhotoDto);
if (!userFromRepo.Photos.Any(p => p.IsMain))
{
photo.IsMain = true;
}
userFromRepo.Photos.Add(photo);
if (await _datingRepository.SaveAll())
{
var photoToReturn = _mapper.Map<ReturnPhotoDto>(photo);
return CreatedAtRoute("GetPhoto", new { id = photo.Id }, photoToReturn);
}
return BadRequest("Could not add photo");
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
Can you please share why do you use open stream? You can try:
var imageuploadParams = new ImageUploadParams () {
File = new FileDescription (#"https://res.cloudinary.com/demo/image/upload/v1561532539/sample.jpg"),
PublicId = "myimage",
Transformation = new Transformation().Width(500).Height(500).Crop("fill").Gravity("face")
};
var ImageuploadResult = cloudinary.Upload (imageuploadParams);
I use the code below to send an image to post method and save it as BLOB in DB and it's working successfully:
angular code:
public postUploadedFile(file:any){
this.formData = new FormData();
this.formData.append('file',file,file.name);
this.Url='http://localhost:38300/api/site/PostUploadFiles';
console.log("url passed from here",this.Url)
return this.http.post(this.Url , this.img).subscribe()
}
API code:
public IHttpActionResult PostUploadFiles()
{
int i = 0;
var uploadedFileNames = new List<string>();
string result = string.Empty;
HttpResponseMessage response = new HttpResponseMessage();
var httpRequest = HttpContext.Current.Request;
if (httpRequest.Files.Count > 0)
{
while(i < httpRequest.Files.Count && result != "Failed")
{
br = new BinaryReader(httpRequest.Files[i].InputStream);
ImageData = br.ReadBytes(httpRequest.Files[i].ContentLength);
br.Close();
if (DB_Operation_Obj.Upload_Image(ImageData) > 0)
{
result = "success";
}
else
{
result = "Failed";
}
i++;
}
}
else
{
result = "can't find images";
}
return Json(result);
}
but now I need to send more info with image ( type id, name) not just the image, so angular code will be like :
public postUploadedFile(file:any, type_id:number,site_id:number){
this.img = new Image_List();
this.img.images = new Array<PreviewURL>();
this.img.type_id= type_id;
this.img.Reference_id = site_id;
this.img.images.push(file);
this.formData = new FormData();
this.formData.append('file',file,file.name);
this.Url='http://localhost:38300/api/site/PostUploadFiles';
console.log("url passed from here",this.Url)
return this.http.post(this.Url , this.img).subscribe()
}
any help to send and insert in DB.
I think you could just make a single upload file method, and make another method for data insert with the file name,so it will be like:
public postUploadedFile(file:any){ this.formData = new FormData(); this.formData.append('file',file,file.name); this.Url='http://localhost:38300/api/site/PostUploadFiles';
This.newMethod(filename);//and here you upload the other data
console.log("url passed from here",this.Url) return this.http.post(this.Url , this.img).subscribe() }
Use FormData to append additional information to api call.
const formData = new FormData();
formData.append(file.name, file,'some-data');
You can use multiple values with the same name.
I have an Asp.net Core method with below definition.
[HttpPost]
public IActionResult Upload(IFormFile file)
{
if (file == null || file.Length == 0)
throw new Exception("file should not be null");
var originalFileName = ContentDispositionHeaderValue
.Parse(file.ContentDisposition)
.FileName
.Trim('"');
file.SaveAs("your_file_full_address");
}
I want to create XUnit Test for this function, how could I mock IFormFile?
Update:
Controller:
[HttpPost]
public async Task<ActionResult> Post(IFormFile file)
{
var path = Path.Combine(#"E:\path", file.FileName);
using (var stream = new FileStream(path, FileMode.Create))
{
await file.CopyToAsync(stream);
}
return Ok();
}
Xunit Test
[Fact]
public async void Test1()
{
var file = new Mock<IFormFile>();
var sourceImg = File.OpenRead(#"source image path");
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
writer.Write(sourceImg);
writer.Flush();
stream.Position = 0;
var fileName = "QQ.png";
file.Setup(f => f.OpenReadStream()).Returns(stream);
file.Setup(f => f.FileName).Returns(fileName);
file.Setup(f => f.Length).Returns(stream.Length);
var controller = new ValuesController();
var inputFile = file.Object;
var result = await controller.Post(inputFile);
//Assert.IsAssignableFrom(result, typeof(IActionResult));
}
But, I got empty image in the target path.
When testing with IFormFile dependencies, mock the minimal necessary members to exercise the test. In the Controller above FileName property and CopyToAsync method are used. Those should be setup for the test.
public async Task Test1() {
// Arrange.
var file = new Mock<IFormFile>();
var sourceImg = File.OpenRead(#"source image path");
var ms = new MemoryStream();
var writer = new StreamWriter(ms);
writer.Write(sourceImg);
writer.Flush();
ms.Position = 0;
var fileName = "QQ.png";
file.Setup(f => f.FileName).Returns(fileName).Verifiable();
file.Setup(_ => _.CopyToAsync(It.IsAny<Stream>(), It.IsAny<CancellationToken>()))
.Returns((Stream stream, CancellationToken token) => ms.CopyToAsync(stream))
.Verifiable();
var controller = new ValuesController();
var inputFile = file.Object;
// Act.
var result = await controller.Post(inputFile);
//Assert.
file.Verify();
//...
}
Though mentioned in the comments that the question is just a demo, the tight coupling to the file system should be abstracted to allow for better flexibility
you can create an actual instance just like that...
bytes[] filebytes = Encoding.UTF8.GetBytes("dummy image");
IFormFile file = new FormFile(new MemoryStream(filebytes), 0, filebytes.Length, "Data", "image.png");
I had to unit test proper jpeg file upload with correct User, so:
private ControllerContext RequestWithFile()
{
var httpContext = new DefaultHttpContext();
httpContext.Request.Headers.Add("Content-Type", "multipart/form-data");
var sampleImagePath = host.WebRootPath + SampleImagePath; //path to correct image
var b1 = new Bitmap(sampleImagePath).ToByteArray(ImageFormat.Jpeg);
MemoryStream ms = new MemoryStream(b1);
var fileMock = new Mock<IFormFile>();
fileMock.Setup(f => f.Name).Returns("files");
fileMock.Setup(f => f.FileName).Returns("sampleImage.jpg");
fileMock.Setup(f => f.Length).Returns(b1.Length);
fileMock.Setup(_ => _.CopyToAsync(It.IsAny<Stream>(), It.IsAny<CancellationToken>()))
.Returns((Stream stream, CancellationToken token) => ms.CopyToAsync(stream))
.Verifiable();
string val = "form-data; name=";
val += "\\";
val += "\"";
val += "files";
val += "\\";
val += "\"";
val += "; filename=";
val += "\\";
val += "\"";
val += "sampleImage.jpg";
val += "\\";
val += "\"";
fileMock.Setup(f => f.ContentType).Returns(val);
fileMock.Setup(f => f.ContentDisposition).Returns("image/jpeg");
httpContext.User = ClaimsPrincipal; //user part, you might not need it
httpContext.Request.Form =
new FormCollection(new Dictionary<string, StringValues>(), new FormFileCollection { fileMock.Object });
var actx = new ActionContext(httpContext, new RouteData(), new ControllerActionDescriptor());
return new ControllerContext(actx);
}
Then in test:
[Fact]
public async void TestUploadFile_byFile()
{
var amountOfPosts = _dbContext.Posts.Count();
var amountOfPics = _dbContext.SmallImages.Count();
sut.ControllerContext = RequestWithFile();
var ret = await sut.UploadNewDogePOST(new Doge.Areas.User.Models.UploadDoge());
var amountOfPosts2 = _dbContext.Posts.Count();
var amountOfPics2 = _dbContext.SmallImages.Count();
Assert.True(amountOfPosts < amountOfPosts2);
Assert.True(amountOfPics < amountOfPics2);
var lastImage = _dbContext.SmallImages.Include(im => im.DogeBigImage).Last();
var sampleImagePath = host.WebRootPath + SampleImagePath;
var b1 = new Bitmap(sampleImagePath).ToByteArray(ImageFormat.Jpeg);
Assert.True(b1.Length == lastImage.DogeBigImage.Image.Length);
Assert.IsType<RedirectToActionResult>(ret);
Assert.Equal("Index", ((RedirectToActionResult)ret).ActionName);
}
I'm using OWIN with WebAPI integration as WebApp. Future plan is to use OWIN self-hosting which is working fine but the OWIN testserver implementation is not working together with RestSharp:
Sample without RestSharp:
https://blogs.msdn.microsoft.com/webdev/2013/11/26/unit-testing-owin-applications-using-testserver/
First attempt is to use a mock class derived from RestClient class:
public class MockRestClient : RestClient
{
public TestServer TestServer { get; set; }
public MockRestClient(TestServer testServer)
{
TestServer = testServer;
}
public override IRestResponse Execute(IRestRequest request)
{
// TODO: Currently the test server is only doing GET requests via RestSharp
var response = TestServer.HttpClient.GetAsync(request.Resource).Result;
var restResponse = ConvertToRestResponse(request, response);
return restResponse;
}
private static RestResponse ConvertToRestResponse(IRestRequest request, HttpResponseMessage httpResponse)
{
RestResponse restResponse1 = new RestResponse();
restResponse1.Content = httpResponse.Content.ReadAsStringAsync().Result;
restResponse1.ContentEncoding = httpResponse.Content.Headers.ContentEncoding.FirstOrDefault();
restResponse1.ContentLength = (long)httpResponse.Content.Headers.ContentLength;
restResponse1.ContentType = httpResponse.Content.Headers.ContentType.ToString();
if (httpResponse.IsSuccessStatusCode == false)
{
restResponse1.ErrorException = new HttpRequestException();
restResponse1.ErrorMessage = httpResponse.Content.ToString();
restResponse1.ResponseStatus = ResponseStatus.Error;
}
restResponse1.RawBytes = httpResponse.Content.ReadAsByteArrayAsync().Result;
restResponse1.ResponseUri = httpResponse.Headers.Location;
restResponse1.Server = "http://localhost";
restResponse1.StatusCode = httpResponse.StatusCode;
restResponse1.StatusDescription = httpResponse.ReasonPhrase;
restResponse1.Request = request;
RestResponse restResponse2 = restResponse1;
foreach (var httpHeader in httpResponse.Headers)
restResponse2.Headers.Add(new Parameter()
{
Name = httpHeader.Key,
Value = (object)httpHeader.Value,
Type = ParameterType.HttpHeader
});
//foreach (var httpCookie in httpResponse.Content.)
// restResponse2.Cookies.Add(new RestResponseCookie()
// {
// Comment = httpCookie.Comment,
// CommentUri = httpCookie.CommentUri,
// Discard = httpCookie.Discard,
// Domain = httpCookie.Domain,
// Expired = httpCookie.Expired,
// Expires = httpCookie.Expires,
// HttpOnly = httpCookie.HttpOnly,
// Name = httpCookie.Name,
// Path = httpCookie.Path,
// Port = httpCookie.Port,
// Secure = httpCookie.Secure,
// TimeStamp = httpCookie.TimeStamp,
// Value = httpCookie.Value,
// Version = httpCookie.Version
// });
return restResponse2;
}
Unfortunatly I stuck with Post events, which needs html body from restResponse.
Has anybody done something similar.
BTW: I can also use OWIN unit tests with self-hosting OWIN, but this will not work on Teamcity automatic builds.
I changed the mock rest Client to work with Post/Put/Delete methods too. It is not 100% complete (missing auth, Cookies, files etc.), but in my case it is sufficient:
public class MockRestClient : RestClient
{
public TestServer TestServer { get; set; }
public MockRestClient(TestServer testServer)
{
TestServer = testServer;
}
public override IRestResponse Execute(IRestRequest request)
{
// TODO: Currently the test server is only doing GET requests via RestSharp
HttpResponseMessage response = null;
Parameter body = request.Parameters.FirstOrDefault(p => p.Type == ParameterType.RequestBody);
HttpContent content;
if (body != null)
{
object val = body.Value;
byte[] requestBodyBytes;
string requestBody;
if (val is byte[])
{
requestBodyBytes = (byte[]) val;
content = new ByteArrayContent(requestBodyBytes);
}
else
{
requestBody = Convert.ToString(body.Value);
content = new StringContent(requestBody);
}
}
else
content = new StringContent("");
string urladd = "";
IEnumerable<string> #params = from p in request.Parameters
where p.Type == ParameterType.GetOrPost && p.Value != null
select p.Name + "=" + p.Value;
if(!#params.IsNullOrEmpty())
urladd = "?" + String.Join("&", #params);
IEnumerable<HttpHeader> headers = from p in request.Parameters
where p.Type == ParameterType.HttpHeader
select new HttpHeader
{
Name = p.Name,
Value = Convert.ToString(p.Value)
};
foreach (HttpHeader header in headers)
{
content.Headers.Add(header.Name, header.Value);
}
content.Headers.ContentType.MediaType = "application/json";
switch (request.Method)
{
case Method.GET:
response = TestServer.HttpClient.GetAsync(request.Resource + urladd).Result;
break;
case Method.DELETE:
response = TestServer.HttpClient.DeleteAsync(request.Resource + urladd).Result;
break;
case Method.POST:
response = TestServer.HttpClient.PostAsync(request.Resource + urladd, content).Result;
break;
case Method.PUT:
response = TestServer.HttpClient.PutAsync(request.Resource + urladd, content).Result;
break;
default:
return null;
}
var restResponse = ConvertToRestResponse(request, response);
return restResponse;
}
private static RestResponse ConvertToRestResponse(IRestRequest request, HttpResponseMessage httpResponse)
{
RestResponse restResponse1 = new RestResponse();
restResponse1.Content = httpResponse.Content.ReadAsStringAsync().Result;
restResponse1.ContentEncoding = httpResponse.Content.Headers.ContentEncoding.FirstOrDefault();
restResponse1.ContentLength = (long)httpResponse.Content.Headers.ContentLength;
restResponse1.ContentType = httpResponse.Content.Headers.ContentType.ToString();
if (httpResponse.IsSuccessStatusCode == false)
{
restResponse1.ErrorException = new HttpRequestException();
restResponse1.ErrorMessage = httpResponse.Content.ToString();
restResponse1.ResponseStatus = ResponseStatus.Error;
}
restResponse1.RawBytes = httpResponse.Content.ReadAsByteArrayAsync().Result;
restResponse1.ResponseUri = httpResponse.Headers.Location;
restResponse1.Server = "http://localhost";
restResponse1.StatusCode = httpResponse.StatusCode;
restResponse1.StatusDescription = httpResponse.ReasonPhrase;
restResponse1.Request = request;
RestResponse restResponse2 = restResponse1;
foreach (var httpHeader in httpResponse.Headers)
restResponse2.Headers.Add(new Parameter()
{
Name = httpHeader.Key,
Value = (object)httpHeader.Value,
Type = ParameterType.HttpHeader
});
//foreach (var httpCookie in httpResponse.Content.)
// restResponse2.Cookies.Add(new RestResponseCookie()
// {
// Comment = httpCookie.Comment,
// CommentUri = httpCookie.CommentUri,
// Discard = httpCookie.Discard,
// Domain = httpCookie.Domain,
// Expired = httpCookie.Expired,
// Expires = httpCookie.Expires,
// HttpOnly = httpCookie.HttpOnly,
// Name = httpCookie.Name,
// Path = httpCookie.Path,
// Port = httpCookie.Port,
// Secure = httpCookie.Secure,
// TimeStamp = httpCookie.TimeStamp,
// Value = httpCookie.Value,
// Version = httpCookie.Version
// });
return restResponse2;
}
I am trying to access the Jira rest API via C#. For that I am using the Windows.Web.Http.HttpClient. But all I get as a return value is HTML.
I am calling the following URL: https://jira.atlassian.com/rest/api/latest/field/
Little Edit
When I call the url from a browser it works fine, just the call from the HttpClient doesn't work.
Here is my code:
public async Task<IRestResponse> Execute(RestRequest request) {
var restResponse = new RestResponse();
var client = new HttpClient();
var req = new HttpRequestMessage(request.Method, new Uri(BaseUrl, UriKind.RelativeOrAbsolute));
foreach (var item in request.headers) {
req.Headers[item.Key] = item.Value;
}
req.Headers.Accept.Add(new HttpMediaTypeWithQualityHeaderValue("application/json"));
if (this.Authenticator != null)
req.Headers["Authorization"] = this.Authenticator.GetHeader();
var res = await client.SendRequestAsync(req);
restResponse.Content = await res.Content.ReadAsStringAsync();
restResponse.StatusCode = res.StatusCode;
restResponse.StatusDescription = res.ReasonPhrase;
if (!res.IsSuccessStatusCode) {
restResponse.ErrorMessage = restResponse.Content;
restResponse.ResponseStatus = ResponseStatus.Error;
} else if (res.StatusCode == HttpStatusCode.RequestTimeout) {
restResponse.ResponseStatus = ResponseStatus.TimedOut;
} else if (res.StatusCode == HttpStatusCode.None) {
restResponse.ResponseStatus = ResponseStatus.None;
} else {
restResponse.ResponseStatus = ResponseStatus.Completed;
}
return restResponse;
}
I just found my problem, I don't add the relative path anywhere. It just calls the BaseUrl meaning https://jira.atlassian.com/ that explains why I get the HTML page.