Per my code below. I'm testing UI to ensure correct permissions for a application are applied to certain user accounts.
When I run the below, the test passes at "Assert.Pass(); //marks as a pass here ok".
Following this, code drops into the catch block.
The exception message is literally blank, the stack trace is:
at NUnit.Framework.Assert.Pass(String message, Object[] args) at
NUnit.Framework.Assert.Pass() at
SigangeCoreUiTests.Tests.PermissionTests.BslUserPermissionTest() in
C:...\PermissionTests.cs:line 90
The test then obviously fails because of the assert fail, however there is no reason for the exception to be raised and it already passed. The screen shot capture is not raising an issue, it seems to be the Assert.Pass.
Why might my code do this?
public void BslUserPermissionTest()
{
var _testUsername = _configuration.GetValue<string>("PermissionsTestUserList:BSLUser:Username");
var _testPassword = _configuration.GetValue<string>("PermissionsTestUserList:BSLUser:Password");
var wait = new WebDriverWait(_driver, new TimeSpan(0, 0, 60));
try
{
if (!LoginHandler(_testUsername, _testPassword))
{
Assert.Fail();
}
System.Threading.Thread.Sleep(2000);
var menuItem = wait.Until(ExpectedConditions.ElementIsVisible(By.LinkText("BSL Interpreter")));
menuItem.Click();
var result = wait.Until(ExpectedConditions.ElementIsVisible(By.TagName("app-bsl-interpreter")));
if (result.Text.Contains("Message for Interpretation"))
{
//test no other menu items are present - only expecting the one menu item
if (ValidateSingleMenuItemPresent())
{
new SupportClasses.ScreenShot().Take(_driver, false);
Assert.Fail();
}
new SupportClasses.ScreenShot().Take(_driver, true);
Assert.Pass(); //marks as a pass here ok
}
else
{
new SupportClasses.ScreenShot().Take(_driver, false);
Assert.Fail();
}
}
catch (Exception ex) //raises exception for unknown reason
{
new SupportClasses.ScreenShot().Take(_driver, false);
Assert.Fail();
}
finally
{
_driver = new SupportClasses.SsdAuthentiation(_driver).Logout();
}
}
Assert.Pass throws exception, you should use Assert to check values as per your test case.
See the below code from GitHub.
/// <summary>
/// Throws a <see cref="SuccessException"/> with the message and arguments
/// that are passed in. This allows a test to be cut short, with a result
/// of success returned to NUnit.
/// </summary>
[DoesNotReturn]
static public void Pass()
{
Assert.Pass(string.Empty, null);
}
Related
I'm writing an unhappy flow unit test.
In my code I setup a function to throw async and later I validate that the function was called once.
This scenario fails and succeeds irregularly, as sometimes it shows no calls were made and sometimes the function is called once (as planned).
Do you know why?
Let me know if any more code is needed.
Thanks!
The test:
[Fact]
public async Task ContainerScan_ControllerTest_ErrorFlow()
{
//Given
TestsRegistrar.ContainerClient.Reset();
TestsRegistrar.ContainerClient.Setup(x => x.MyAsyncFunc(It.IsAny<Guid>(), It.IsAny<List<ContainerScanRecord>>())).ThrowsAsync(new Exception("bad stuff happened"));
var controller = new ContainersRiskGeneratorController(TestsRegistrar.ContainersGenerator, new Mock<IExtendedLogger>().Object, new Mock<IExtendedLoggerEnricher>().Object);
var scanId = Guid.NewGuid();
//When
var result = await controller.SomeUpperFuncthatCallsMyAsyncFunc(scanId, new RiskGenerator.Dto.V3.ContainersScanRequest
{
ContainerRecords = new List<RiskGenerator.Dto.V3.SomeDto>
{
new RiskGenerator.Dto.V3.SomeDto{
params here
}
}
});
//Then
result.Should().BeEmpty();
TestsRegistrar.ContainerClient.Verify(x => x.MyAsyncFunc(It.IsAny<Guid>(), It.IsAny<List<ContainerScanRecord>>()), Times.Once, "Container Engine should be called once");
}
UDPATE #1: added the calling funcs
The code for SomeUpperFuncthatCallsMyAsyncFunc:
[HttpPost("someUrl/{scanId:Guid?}")]
[DisableRequestSizeLimit]
public async Task<IReadOnlyCollection<SomeDto>> SomeUpperFuncthatCallsMyAsyncFunc(Guid? _, [FromBody] ContainersScanRequest request)
{
try
{
Ensure.NotNull(nameof(request), request);
_extendedLoggerEnricher.Enrich(new { request.ScanId });
_logger.Info("GenerateContainerRiskAsync called", new { request.ScanId });
var response =
await _riskGenerator.TheCallingFuncToMyAsyncFunc(
request.ScanId,
request.ContainerRecords
.Where(x=> !string.IsNullOrEmpty(x.DockerFilePath))
.Select(ContainerScanRiskRecordDto.ToContainerScanRecord).ToList())
.ConfigureAwait(false);
return response.Select(VulnerableContainerScanRecordDto.ToContainerScanVulnerablerRecord).ToArray();
}
catch (Exception exception)
{
_logger.Error("Exception thrown during generate GenerateContainerRiskAsync", exception, new { request.ScanId });
StatusCode((int)HttpStatusCode.InternalServerError);
return new List<SomeDto>();
}
}
And the func that calls directly to MyAsyncFunc:
public async Task<List<(ContainerScanRecord record, ImageVulnerabilitiesDto vulnerabilities)>> TheCallingFuncToMyAsyncFunc(Guid id, IReadOnlyCollection<ContainerScanRecord> containerRecords)
{
try
{
return await
_containerEngineClient.MyAsyncFunc(id, containerRecords).ConfigureAwait(false);
}
catch (Exception exception)
{
_logger.Error($"[{id}] Could not generate GenerateContainerRiskDataAsync: {exception.Message}", exception);
throw;
}
}
UDPATE #2: figured it out!
Well... it turned out to be a nasty race condition with some other class that initialized the data.
That's why it happened randomly.
Sorry for the hustle and thank you all for your time.
As I found out recently, when function throws, its invocation can not be counted. So this is why you may experience such problems.
What you could do is to verify that logger logs an error (the same way you assert that your method was called).
I'm writing my first Unit test for very small project. Here the expected result and result both return a ArgumentNullException but the test still fails. Any idea why?
[TestMethod]
public void InsertFileBeginning_FilePathNull_ReturnArgumentNullException()
{
// Arrange
var generateFile = new GenerateFile();
string parameter = null; //pass FilePath Null
var expectedExcetpion = new ArgumentNullException();
// Act & Assert
var result = Assert.ThrowsException<ArgumentNullException>(() => generateFile.InsertFileBeginning(parameter));
Assert.AreEqual(expectedExcetpion, result);
}
------InsertFileBeginning function--------
public void InsertFileBeginning(string filePath)
{
try
{
using (var fs = new FileStream(filePath, FileMode.Create))
{
Byte[] metadata = new UTF8Encoding(true).GetBytes("THis is a test content");
fs.Write(metadata, 0, metadata.Length);
}
}
catch (Exception exception)
{
throw exception;
}
}
Error:
Expected: System.ArgumentNullException: Value cannot be null.
Actual: System.ArgumentNullException: Path cannot be null. Parameter name: path
Message: Assert.AreEqual failed. Expected:<System.ArgumentNullException: Value cannot be null.>. Actual:<System.ArgumentNullException: Path cannot be null.
Parameter name: path
at SmartTestSelecter.GenerateFile.InsertFileBeginning(String filePath) in C:\Users\CC\SmartTestSelecter\GenerateFile.cs:line 31
at SmartTestSelecterUnitTests.GenerateFileTest.<>c__DisplayClass0_0.<InsertFileBeginning_FilePathNull_ReturnArgumentNullException>b__0() in C:\Users\CC\STSUnitTests\GenerateFileTest.cs:line 21
at Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsException[T](Action action, String message, Object[] parameters)>.
Look at this;
var expectedExcetpion = new ArgumentNullException();
// Act & Assert
var result = Assert.ThrowsException<ArgumentNullException>(() => generateFile.InsertFileBeginning(parameter));
Assert.AreEqual(expectedExcetpion, result);
expectedException is an Object of type ArgumentNullException and result is also an object of type ArgumentNullException - however they're not the same object! you have 2 instances of the same type.
Now AreEqual(..) uses .Equals from what i could gather online.
I think that you're comparing the references of expectedException with result here. They are of course not the same. What you should instead do (if my assumptions are right) is check if the result is of the same type, rather than use AreEqual(..).
It seems you can use this method for that:
Assert.IsInstanceOfType
https://learn.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.testtools.unittesting.assert.isinstanceoftype?view=mstest-net-1.2.0
e.g.:
Assert.IsInstanceOfType(result, typeof(ArgumentNullException));
First of all, do not use [ExpectedException]. It turned out to be a bad practice because the exception can occur anywhere. And since you use Assert.ThrowsException, which does not throws the exception further, your test would fail anyway.
Secondly, I'm not quite up-to-date regarding MSTest but it seems it fails if the exception is not thrown with the default message. But if you can't specify the expected error message in Assert.ThrowsException, then you can implement your own assert method:
public static void Throws<T>(Action action, string expectedMessageContent = null)
where T : Exception
{
try
{
action.Invoke();
}
catch (Exception e)
{
Assert.IsInstanceOf(typeof(T), e);
Assert.IsTrue(expectedMessageContent == null
|| e.Message.Contains(expectedMessageContent), $"Expected message: {expectedMessageContent}{Environment.NewLine}Actual message:{e.Message}");
return;
}
Assert.Fail("No exception was thrown");
}
Disclaimer: I don't know whether MSTest has Assert.IsInstanceOf, etc methods but you see the point.
I am developing in C# for the Motorola device "MC67" and I have having issues with initialising the scanner.
The code I am using seems to be generic as I have found similar examples all over the Internet; for reference here is the code that is causing me problems:
/// <summary>
/// Initialize the reader.
/// </summary>
///
public override bool InitReader()
{
Logger.Instance.Debug("InitReader");
bool result = false;
// Logger.Instance.AddToDebuggerLog("Symbol.InitReader");
// If reader is already present then fail initialize
if (this._MyReader != null)
{
return false;
}
try
{
// Create new reader, first available reader will be used.
this._MyReader = new Symbol.Barcode.Reader();
// Create reader data
this._MyReaderData = new Symbol.Barcode.ReaderData(
Symbol.Barcode.ReaderDataTypes.Text,
Symbol.Barcode.ReaderDataLengths.MaximumLabel);
// Enable reader, with wait cursor
this._MyReader.Actions.Enable();
if ((GetDeviceType() != DeviceTypes.SymbolMC3070) && (GetDeviceType() != DeviceTypes.SymbolMC3090BT))
{
this._MyReader.Parameters.Feedback.Success.BeepTime = 0;
}
else
{
this._MyReader.Parameters.Feedback.Success.BeepTime = 50;
}
SetScannerDecoderTypeToUseWithScanSys();
result = true;
}
catch (Exception ex)
{
// Something has gone wrong Initializing barcode reader etc
// Log Exception
Logger.Instance.Exception("InitReader", ex);
// Ensure reader is Disposed
if (_MyReader != null)
{
try
{
_MyReader.Dispose();
}
catch
{
// Just incase something goes wrong
Logger.Instance.Error("Error Disposing MyReader in InitReader Exception");
}
_MyReader = null;
}
// Ensure ReaderData is Disposed
if (_MyReaderData != null)
{
try
{
_MyReaderData.Dispose();
}
catch
{
// Just incase something goes wrong
Logger.Instance.Error("Error Disposing MyReaderData in InitReader Exception");
}
_MyReaderData = null;
}
// null the EventHandler
_MyEventHandler = null;
}
return result;
}
My problem is that when the above method is called, the following line produces an exception error:
this._MyReader.Actions.Enable();
The exception is "OperationFailureException" and the error message mentions "Get all supported attributes failed : E_SCN_INVALIDIOCTRL"
Now the strange thing is that I am able to actually use the scanner on the device correctly, so I can scan barcodes and read the data even with this exception but the fact that it is happening concerns me so I am trying to prevent it.
Does anyone have any idea why I am getting the exception or any suggestions of things I can try?
This is a "handled" exception in the Symbol library. Just turn off the breakpoint for thrown exception-- Ctrl-Alt-E, in the row "Common Language Runtime Exceptions" uncheck the box under "Thrown". Unfortunately if you're trying to debug an exception that isn't working correctly, you just gotta keep pressing play every time this exception comes up.
I haven't found a way to make it stop throwing the exception though... I'd really like to be able to turn off whatever feature is failing.
PREFACE: I know the code excerpt is lengthy but I did't want to leave out a detail that someone else might spot as the cause of the issue. The reason for the somewhat verbose nature of the code and the many Exception traps is due to the hunt for the NullReferenceException that I describe below. You can wade through the code quickly to the salient parts by jumping to the await keywords found amongst the async methods that call each other.
UPDATE: The InvalidOperationException is occurring because I am altering the IsEnabled status of certain buttons. I am on the Main thread so I am not sure why this is happening. Does anyone know why?
I have a Windows Phone 7 application written C# that is getting a System.InvalidOperationException when GetResponseAsync() is called in a particular code context. The application uses the PetFinder API to create a Cat Breed Guessing game with the intention of helping cats in animal shelters get adopted. Here is the exact Exception message in its entirety:
Message: An unhandled exception of type 'System.InvalidOperationException' occurred in System.Windows.ni.dll
Before the Exception occurs, there are several successful calls to GetResponseAsync(). I have included the code for the methods involved in the Exception in the order that they are called below. Can someone tell me why I am getting this Exception and how to fix it?
The Exception is occurring completely out of the current code context that calls it, so it's some interaction between the code below and the library that contains GetResponseAsync() that is creating the conditions for the problem. The thread code context just before the call to GetResponseAsync() is the Main thread.
BACKGROUND NOTE:
This all began while I was chasing down a NullReferenceException that was occuring during the call to getRandomPetExt() within doLoadRandomPet(). From the reading I did on SO, my guess is that a NULL Task was being returned from getRandomPetExt(). But if you look at that code you'll see I'm doing everything in my power to trap a spurious Exception and to avoid returning a NULL Task. My current belief still at this time is that the NULL Task is occurring because some other code is generating a spurious Exception outside of my code. Perhaps something in Microsoft.Bcl.Async? Is this some strange synchronization context problem or hidden cross-thread access issue?
The strange part is that before I made a particular change I did not get the InvalidOperationException at all, only the intermittent NullReferenceException every 1 out of 20 to 30 calls to the method chain shown below. The InvalidOperationException on the other hand happens every time with the new code structure. The change I made was to me a minor one meant to help my debugging efforts. The only thing I did was create a method wrapper that moved the guts of loadRandomPet() into doLoadRandomPet(). I did this so I could disable some buttons that triggered method calls that might interfere with the operation to get a random pet. I wrapped the call to doLoadRandomPet() in a try/finally block, to make sure the buttons were re-enabled when the operation exited. Why would this cause such a major change in code execution?
async private void loadRandomPet(int maxRetries = 3)
{
// Do not allow the Guess My Breed or Adopt Me buttons to be
// clicked while we are getting the next pet.
btnAdoptMe.IsEnabled = false;
btnGuessMyBreed.IsEnabled = false;
try
{
await doLoadRandomPet(maxRetries);
}
finally
{
// >>>>> THIS CODE IS NEVER REACHED.
// Make sure the buttons are re-enabled.
btnAdoptMe.IsEnabled = true;
btnGuessMyBreed.IsEnabled = true;
}
}
// -------------------- CALLED NEXT
/// <summary>
/// (awaitable) Loads a random pet with a limit on the number of retries in case of failure.
/// </summary>
/// <param name="maxRetries">The number of retries permitted.</param>
async private Task doLoadRandomPet(int maxRetries = 3)
{
// Show the busy indicator.
radbusyMain.Visibility = Visibility.Visible;
try
{
// Get a random pet.
List<KeyValuePair<string, string>> listUrlArgs = new List<KeyValuePair<string, string>>();
// Only cats.
listUrlArgs.addKVP("animal", PetFinderUtils.EnumAnimalType.cat.ToString());
if (!String.IsNullOrWhiteSpace(MainMenu.ZipCode))
{
listUrlArgs.addKVP(PetFinderUtils.URL_FIELD_LOCATION, MainMenu.ZipCode);
}
if (maxRetries < 0)
throw new ArgumentOutOfRangeException("The maximum retries value is negative.");
Debug.WriteLine("------------------ START: LOADING Random Pet ----------------");
// Loop until a random pet is found.
int numRetries = 0;
// Select the breed, otherwise we will get a ton of "Domestic Short Hair" responses,
// which are not good for the game. Breeds that are returning empty search
// results this session are filtered too.
string strBreedName = MainMenu.GetRandomBreedName();
listUrlArgs.addKVP("breed", strBreedName);
while (numRetries <= maxRetries)
{
try
{
// Save the last successful retrieval.
if (this._pbi != null)
_pbiLast = this._pbi;
this._pbi = await getRandomPetExt(listUrlArgs);
}
catch (EmptySearchResultsException esr)
{
// getRandomPetExt() could not find a suitable cat given the current parameters.
// Swallow the Exception without notifying the user. Just allow the code
// further down to re-use the last cat retrieved in the hopes the next
// quiz won't have the problem.
Debug.WriteLine(">>>>>>>>>> doLoadRandomPet() - getRandomPet() failed to find a cat.");
}
catch (PetFinderApiException pfExc)
{
if (pfExc.ResponseCode == PetFinderUtils.EnumResponseCodes.PFAPI_ERR_LIMIT)
// Swallow the Exception, but let the user know to stop playing for the awhile
// since we have exceeded our rate limit.
CatQuizAux.EasyToast("The PetFinder server is busy.\nPlease try playing the game\nlater.");
else
// Swallow the Exception, but let the user know to stop playing for the awhile
// since we have exceeded our rate limit.
CatQuizAux.EasyToast("The PetFinder may be down.\nPlease try playing the game\nlater.");
// Just exit.
return;
} // try
catch (Exception exc)
{
// This is really bad practice but we're out of time. Just swallow the Exception
// to avoid crashing the program.
Debug.WriteLine(">>>>>>>>>> doLoadRandomPet() - getRandomPet() Other Exception occurrred. Exception Message: " + exc.Message);
}
// If the returned pet is NULL then no pets using the current search criteria
// could be found.
if (this._pbi != null)
{
// Got a random pet, stop looping. Save it to the backup cat field too.
break;
}
else
{
// Are we using a location?
if (listUrlArgs.hasKey(PetFinderUtils.URL_FIELD_LOCATION))
// Retry without the location to open the search to the entire PetFinder API
// inventory.
listUrlArgs.deleteKVP(PetFinderUtils.URL_FIELD_LOCATION);
else
{
// Use a differet breed. Add the current breed to the list of breeds returning
// empty search results so we don't bother with that breed again this session.
MainMenu.ListEmptyBreeds.Add(strBreedName);
// Remove the current breed.
listUrlArgs.deleteKVP("breed");
// Choose a new breed.
strBreedName = MainMenu.GetRandomBreedName();
listUrlArgs.addKVP("breed", strBreedName);
} // else - if (listUrlArgs.hasKey(PetFinderUtils.URL_FIELD_LOCATION))
} // if (this._pbi == null)
// Sleep a bit.
await TaskEx.Delay(1000);
numRetries++;
} // while (numRetries <= maxRetries)
// If we still have a null _pbi reference, use the back-up one.
if (this._pbi == null)
this._pbi = this._pbiLast;
if (this._pbi == null)
throw new ArgumentNullException("(ViewPetRecord::doLoadRandomPet) Failed completely to find a new cat for the quiz. Please try again later.");
// Add the pet to the already quizzed list.
MainMenu.AddCatQuizzed(this._pbi.Id.T.ToString());
// Show the cat's details.
lblPetName.Text = this._pbi.Name.T;
imgPet.Source = new BitmapImage(new Uri(this._pbi.Media.Photos.Photo[0].T, UriKind.Absolute));
// Dump the cat's breed list to the Debug window for inspection.
dumpBreedsForPet(this._pbi);
}
finally
{
// Make sure the busy indicator is hidden.
radbusyMain.Visibility = Visibility.Collapsed;
}
} // async private void doLoadRandomPet(int maxRetries = 3)
// -------------------- CALLED NEXT
/// <summary>
/// Gets a Random Pet. Retries up to maxRetries times to find a pet not in the already <br />
/// quizzed list before giving up and returning the last one found. Also skips pets without <br />
/// photos.
/// </summary>
/// <param name="listUrlArgs">A list of URL arguments to pass add to the API call.</param>
/// <param name="maxRetries">The number of retries to make.</param>
/// <returns>The basic info for the retrieved pet or NULL if a pet could not be found <br />
/// using the current URL arguments (search criteria).</returns>
async private Task<PetBasicInfo> getRandomPetExt(List<KeyValuePair<string, string>> listUrlArgs, int maxRetries = 3)
{
PetBasicInfo newPbi = null;
try
{
newPbi = await doGetRandomPetExt(listUrlArgs, maxRetries);
}
catch (Exception exc)
{
Debug.WriteLine(">>>>>> (ViewPetRecord::getRandomPetExt) EXCEPTION: " + exc.Message);
throw;
} // try/catch
return newPbi;
} // async private void getRandomPetExt()
// -------------------- CALLED NEXT
// This was done just to help debug the NullReferenceException error we are currently fighting.
// see getRandomPetExt() below.
async private Task<PetBasicInfo> doGetRandomPetExt(List<KeyValuePair<string, string>> listUrlArgs, int maxRetries = 3)
{
if (maxRetries < 0)
throw new ArgumentOutOfRangeException("The maximum retries value is negative.");
Debug.WriteLine("------------------ START: Getting Random Pet ----------------");
// Loop until a random pet is found that has not already been used in the quiz or until
// we hit the maxRetries limit.
int numRetries = 0;
PetBasicInfo pbi = null;
while (numRetries <= maxRetries)
{
try
{
pbi = await MainMenu.PetFinderAPI.GetRandomPet_basic(listUrlArgs);
}
catch (PetFinderApiException pfExcept)
{
// pfExcept.ResponseCode = PetFinderUtils.EnumResponseCodes.PFAPI_ERR_LIMIT;
switch (pfExcept.ResponseCode)
{
case PetFinderUtils.EnumResponseCodes.PFAPI_ERR_NOENT:
Debug.WriteLine("The PetFinder API returned an empty result set with the current URL arguments.");
// No results found. Swallow the Exception and return
// NULL to let the caller know this.
return null;
case PetFinderUtils.EnumResponseCodes.PFAPI_ERR_LIMIT:
Debug.WriteLine("The PetFinder API returned a rate limit error.");
// Throw the Exception. Let the caller handler it.
throw;
default:
// Rethrow the Exception so we know about it from the crash reports.
// Other Exception. Stop retrying and show the user the error message.
Debug.WriteLine("Exception during getRandomPetExt()\n" + pfExcept.ErrorMessage);
throw;
} // switch()
}
// Does the pet have a photo?
if (pbi.Media.Photos.Photo.Length > 0)
{
// Yes. Has the pet already been used in a quiz?
if (!MainMenu.IsCatQuizzed(pbi.Id.T.ToString()))
// No. Success.
return pbi;
} // if (pbi.Media.Photos.Photo.Length > 0)
// Retry required.
Debug.WriteLine(String.Format("Retrying, retry count: {0}", numRetries));
// No photo or already used in a quiz. Wait a little before retrying.
await TaskEx.Delay(1000);
// Count retires.
numRetries++;
} // while (numRetries <= maxRetries)
// Unable to find a cat not already quizzed. Just return the last retrieved.
Debug.WriteLine("Retry count exceeded. Returning last retreived pet.");
// Returning NULL results in a await throwing a non-specific NullReferenceException.
// Better to throw our own Exception.
throw new EmptySearchResultsException("(ViewPetRecord::getRandomPetExt) Unable to retrieve a new random cat from the PetFinder API server.");
// return pbi;
} // async private PetBasicInfo doGetRandomPetExt()
// ------------------ CALLED NEXT
/// <summary>
/// Returns the basic information for a randomly chosen pet of the given animal type.
/// </summary>
/// <param name="enAnimalType">The desired animal type to restrict the search to.</param>
/// <returns></returns>
async public Task<JSON.JsonPetRecordTypes.PetBasicInfo> GetRandomPet_basic(List<KeyValuePair<string, string>> urlArgumentPairs = null)
{
Debug.WriteLine("(GetRandomPet_basic) Top of call.");
// If the URL Argument Pairs parameter is null, then create one.
if (urlArgumentPairs == null)
urlArgumentPairs = new List<KeyValuePair<string, string>>();
// Add the "output" parameter that tells PetFinder we want the Basic information for the pet,
// not the ID or full record.
urlArgumentPairs.addKVP("output", "basic");
// Add a unique URL argument to defeat URL caching that may be taking
// place in the Windows Phone library or at the PetFinder API server.
// This defeats the problem so that a new random pet is returned
// each call, instead of the same one.
long n = DateTime.Now.Ticks;
urlArgumentPairs.addKVP("nocache", n.ToString());
// Build the API call.
string strApiCall =
buildPetFinderApiUrl(METHOD_RANDOM_PET,
urlArgumentPairs);
Debug.WriteLine("(GetRandomPet_basic) URL for call: \n" + strApiCall);
// Make the call.
string strJsonReturn = await Misc.URLToStringAsyncEZ(strApiCall);
bool bIsJsonReturnValid = false;
try
{
JSON.JsonPetRecordTypes.PetRecordBasicInfo jsonPetBasic = JsonConvert.DeserializeObject<JSON.JsonPetRecordTypes.PetRecordBasicInfo>(strJsonReturn);
// Deserialization succeeded.
bIsJsonReturnValid = true;
// Success code?
// For some strange reason T is cast to an "int" here where in GetBreedList it's equivalent is cast to a string.
int iResponseCode = jsonPetBasic.Petfinder.Header.Status.Code.T;
if (iResponseCode != 100)
throw new PetFinderApiException("PetFinder::GetRandomPet_basic", iResponseCode);
// throw new Exception("(PetFinder::GetRandomPet_basic) The response document contains a failure response code: " + iResponseCode.ToString() + ":" + jsonPetBasic.Petfinder.Header.Status.Message);
// Return the pet record basic info.
return jsonPetBasic.Petfinder.Pet;
}
finally
{
if (!bIsJsonReturnValid)
// Setting debug trap to inspect JSON return.
Debug.WriteLine("JSON Deserialization failure.");
Debug.WriteLine("(GetRandomPet_basic) BOTTOM of call.");
} // try/finally
}
// -------------------- CALLED NEXT, never returns
/// <summary>
/// (awaitable) Simpler version of above call. Same warnings about getting byte stream <br />
/// objects apply here as they do to URLtoStringAsync()
/// </summary>
/// <param name="stUrl"></param>
/// <param name="reqMethod"></param>
/// <returns></returns>
async public static Task<string> URLToStringAsyncEZ(string strUrl, HttpRequestMethod reqMethod = HttpRequestMethod.HTTP_get)
{
strUrl = strUrl.Trim();
if (String.IsNullOrWhiteSpace(strUrl))
throw new ArgumentException("(Misc::URLToStringAsyncEZ) The URL is empty.");
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(strUrl);
// Get the string value for the request method.
request.Method = reqMethod.GetDescription();
// >>>>> THIS CALL to GetResponseAsync() TRIGGERS THE EXCEPTION (see stack trace below)
// Async wait for the respone.
HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync();
// Use a stream reader to return the string.
using (var sr = new StreamReader(response.GetResponseStream()))
{
return sr.ReadToEnd();
}
}
// -------------------- STACK TRACE JUST BEFORE URLToStringAsync(), the call the triggers the exception.
> Common_WP7.DLL!Common_WP7.Misc.URLToStringAsyncEZ(string strUrl, Common_WP7.Misc.HttpRequestMethod reqMethod) Line 1079 C#
CatQuiz.DLL!CatQuiz.PetFinderUtils.GetRandomPet_basic(System.Collections.Generic.List<System.Collections.Generic.KeyValuePair<string,string>> urlArgumentPairs) Line 441 C#
CatQuiz.DLL!CatQuiz.ViewPetRecord.doGetRandomPetExt(System.Collections.Generic.List<System.Collections.Generic.KeyValuePair<string,string>> listUrlArgs, int maxRetries) Line 55 C#
CatQuiz.DLL!CatQuiz.ViewPetRecord.getRandomPetExt(System.Collections.Generic.List<System.Collections.Generic.KeyValuePair<string,string>> listUrlArgs, int maxRetries) Line 123 C#
CatQuiz.DLL!CatQuiz.ViewPetRecord.doLoadRandomPet(int maxRetries) Line 243 C#
CatQuiz.DLL!CatQuiz.ViewPetRecord.loadRandomPet(int maxRetries) Line 343 C#
CatQuiz.DLL!CatQuiz.ViewPetRecord.PageViewPetRecord_Loaded(object sender, System.Windows.RoutedEventArgs e) Line 355 C#
System.Windows.ni.dll!MS.Internal.CoreInvokeHandler.InvokeEventHandler(int typeIndex, System.Delegate handlerDelegate, object sender, object args) Unknown
System.Windows.ni.dll!MS.Internal.JoltHelper.FireEvent(System.IntPtr unmanagedObj, System.IntPtr unmanagedObjArgs, int argsTypeIndex, int actualArgsTypeIndex, string eventName) Unknown
======================= EXCEPTION
// -------------------- STACK TRACE when EXCEPTION occurs
> CatQuiz.DLL!CatQuiz.App.Application_UnhandledException(object sender, System.Windows.ApplicationUnhandledExceptionEventArgs e) Line 101 C#
System.Windows.ni.dll!MS.Internal.Error.CallApplicationUEHandler(System.Exception e) Unknown
System.Windows.ni.dll!MS.Internal.Error.IsNonRecoverableUserException(System.Exception ex, out uint xresultValue) Unknown
System.Windows.ni.dll!MS.Internal.JoltHelper.FireEvent(System.IntPtr unmanagedObj, System.IntPtr unmanagedObjArgs, int argsTypeIndex, int actualArgsTypeIndex, string eventName) Unknown
// -------------------- CODE CONTEXT when EXCEPTION occurs
// Code to execute on Unhandled Exceptions
private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
{
if (System.Diagnostics.Debugger.IsAttached)
{
// An unhandled exception has occurred; break into the debugger
System.Diagnostics.Debugger.Break();
}
}
The usual advice when rethrowing an exception is to use a throw; statement so the original stack trace is preserved. (Example)
However, when I try this simple example, the Visual Studio debugger does not show the original stack trace.
namespace ExceptionTest
{
class Program
{
static void ThrowException()
{
throw new System.Exception(); // The line that I WANT the debugger to show.
}
static void Main(string[] args)
{
try
{
ThrowException();
}
catch (System.Exception)
{
System.Console.WriteLine("An exception was thrown.");
throw; // The line that the debugger ACTUALLY shows.
}
}
}
}
How can I use the debugger to find the original source of the exception?
Your best option is to ask Visual Studio to break on the original exception rather than navigate back to it from the stack trace. To do this:
1) Click on the 'Debug' menu item
2) Click 'Exceptions...'
3) Select 'Common Language Runtime Exceptions' - 'Thrown'
With this approach you may get more than you really wanted if there are many exceptions being thrown. You can filter which exceptions it breaks on by expanding the tree list.
See image:
If you're running Visual Studio 2010 Ultimate, use IntelliTrace.
It keeps a record of all exceptions thrown and allows you to "debug back in time" to see parameters, threads, and variables at the time of each throw.
(Taken from Chris Schmich's answer to a similar question.)
The best solution that I've found is write the Exception callstack to the Debug.Console and then let the built-in code line parser in Visual Studio to provide the navigation.
I found it really useful when dealing with unhandled exceptions on the AppDomain and WPF Dispatcher as Visual Studio always breaks too late.
Based from an article on Code Project, I have modified it that it outputs to the Console as a single block of text - rather than line-by-line - which was necessary I have logging also writing to the Console.
Usage
public void ReportException(Exception exception)
{
if (Debugger.IsAttached)
{
DebugHelper.PrintExceptionToConsole(exception);
Debugger.Break();
}
// ...
}
Source
public static class DebugHelper
{
// Original idea taken from the CodeProject article
// http://www.codeproject.com/Articles/21400/Navigating-Exception-Backtraces-in-Visual-Studio
private static readonly string StarSeparator = new String('*', 80);
private static readonly string DashSeparator = new String('-', 80);
private const string TabString = " ";
/// <summary>
/// Prints the exception using a format recognized by the Visual Studio console parser.
/// Allows for quick navigation of exception call stack.
/// </summary>
/// <param name="exception">The exception.</param>
public static void PrintExceptionToConsole(Exception exception)
{
using (var indentedTextWriter = new IndentedTextWriter(Console.Out, TabString))
{
var indentLevel = 0;
while (exception != null)
{
indentedTextWriter.Indent = indentLevel;
indentedTextWriter.Write(FormatExceptionForDebugLineParser(exception));
exception = exception.InnerException;
indentLevel++;
}
}
}
private static string FormatExceptionForDebugLineParser(Exception exception)
{
StringBuilder result = new StringBuilder();
result.AppendLine(StarSeparator);
result.AppendLineFormat(" {0}: \"{1}\"", exception.GetType().Name, exception.Message);
result.AppendLine(DashSeparator);
// Split lines into method info and filename / line number
string[] lines = exception.StackTrace.Split(new string[] { " at " }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Trim())
.Where(x => !String.IsNullOrEmpty(x))
.ToArray();
foreach (var line in lines)
{
string[] parts = line.Split(new string[] { " in " }, StringSplitOptions.RemoveEmptyEntries);
string methodInfo = parts[0];
if (parts.Length == 2)
{
string[] subparts = parts[1].Split(new string[] { ":line " }, StringSplitOptions.RemoveEmptyEntries);
result.AppendLineFormat(" {0}({1},1): {2}", subparts[0], Int32.Parse(subparts[1]), methodInfo);
}
else
result.AppendLineFormat(" {0}", methodInfo);
}
result.AppendLine(StarSeparator);
return result.ToString();
}
}
To use the above, as is, you will also need the Extension Method below and add the System.CodeDom.Compiler namespace for IndentedTextWriter.
Extension Method
/// <summary>
/// Appends the string returned by processing a composite format string followed by the default line terminator.
/// </summary>
/// <param name="sb">The StringBuilder.</param>
/// <param name="format">The format.</param>
/// <param name="args">The args.</param>
public static void AppendLineFormat(this StringBuilder sb, string format, params object[] args)
{
sb.AppendFormat(format, args);
sb.AppendLine();
}
You can use the DebuggerNonUserCode Attribute.
See http://blogs.msdn.com/b/jmstall/archive/2007/02/12/making-catch-rethrow-more-debuggable.aspx
The example becomes like this:
namespace ExceptionTest
{
class Program
{
static void ThrowException()
{
throw new System.Exception(); // The line that I WANT the debugger to show.
}
[DebuggerNonUserCode()]
static void Main(string[] args)
{
try
{
ThrowException();
}
catch (System.Exception)
{
System.Console.WriteLine("An exception was thrown.");
throw; // The line that the debugger ACTUALLY shows.
}
}
}
}
Incidentally, in vb.net, one can use exception filters in some situations like this where one knows one isn't really interested in catching an exception--just finding out that it happened. If the code were written in vb.net and used a filter to capture the exception (probably doing the output itself within the 'finally' block) there wouldn't be a "catch and rethrow"--the cursor would jump to the source of the original exception (as a bonus, vs would interrupt the program flow before any stack unwinding took place). Note that one can opt to have vs trap every time a particular exception is thrown, whether or not it will be caught, but sometimes one is only interested in some of the places an exception is thrown, rather than all of them.