C# exception handling under unit testing doesn't work - c#

I'm new in VisualStudio, so I haven't got much experience with the unit testing in it.
I try to write a basic file reader class, wich has two variables: path and lines string list.
The path can be given to the constructor, or also can be set with a setter method.
There is a readIn function, which load up the list with the lines of the text file can be found on the path.
The function should chek if the path is not null or is it valid (is the path pointing to an existing file). If not, it throws a System.ArgumentNullException or a throw new System.ArgumentException.
When I try to test that the exceptions are thrown in case of the path is null or not valid, the VisualStudio always stuck and says that I have an unhadnled exception, however, I defined the expected exception before the test method.
Have you got any idea how should I do this in correct way?
Or if it is the correct way (I read about it also here and on the msdn page too, so it should work), what should I do to get it work?
The reader class:
using System;
using System.Collections.Generic;
using System.IO;
namespace LogParserConsole
{
public class FileReader
{
private string path;
private List<string> lines;
public FileReader()
{
path = null;
lines = null;
}
public FileReader(string path)
{
this.path = path;
if (isFileExists())
{
lines = new List<string>();
}
else
{
lines = null;
}
}
public void readIn()
{
if(path == null)
{
throw new System.ArgumentNullException("The path is not set. Please set a path first!");
}
else if(!isFileExists())
{
throw new System.ArgumentException("The given path is invalid, please chek it!");
}
string line;
System.IO.StreamReader file = new System.IO.StreamReader(path);
while ((line = file.ReadLine()) != null)
{
lines.Add(line);
Console.WriteLine(line);
}
file.Close();
}
public bool isFileExists()
{
return File.Exists(path);
}
public string getPath()
{
return this.path;
}
public List<string> getLines()
{
return this.lines;
}
public void setPath(string path)
{
this.path = path;
}
}
}
The unit test method for the exception:
[TestMethod]
[ExpectedException(typeof(System.ArgumentNullException))]
public void ReadInTest()
{
FileReader fr = new FileReader();
fr.readIn();
}
I also tried an example from the msdn blog page about the expected exceptions (https://blogs.msdn.microsoft.com/csell/2006/01/14/expectedexception-might-not-be-what-youve-expected/), but it also stopped at the exception and after the test failed.
It happened with this simple example code:
[TestMethod, ExpectedException( typeof ( ApplicationException ) ) ]
public void ut1()
{
throw new ApplicationException();
}
I think I should check my preferencies, but have no idea what should I search for.
Thanks for any advice!

Related

Don't know how to fix "The non-generic type 'ArrayList' cannot be used with type arguments"?

I'm trying to read a text file into an ArrayList. If I include in this line: " ArrayList<String> messages = new ArrayList();", then I get the non-generic error, but if I remove it then I get this error on the return line:
Cannot implicitly convert type 'System.Collections.ArrayList'
using System.Collections;
using System;
using System.IO;
namespace mhpreader
{
internal class NewBaseType
{
public ArrayList messages = new ArrayList();
internal NewMhpReader ReadMessages()
{
{
throw new NotImplementedException();
}
}
}
internal class NewMhpReader : NewBaseType
{
private string _FilePath;
public NewMhpReader(string FilePath)
{
this._FilePath = FilePath;
}
private string line;
public NewMhpReader[] ReadMessages(string nMessages)
{
ArrayList<String> messages = new ArrayList();
//List<TextReader> messages = new List<string>;
using (StreamReader stre = new StreamReader(_FilePath))
{
while ((line = stre.ReadLine()) != null)
{
messages.Add(line);
Console.WriteLine(messages);
}
}
return messages;
}
}
}
A couple of things to help you out a bit:
I would remove ArrayList as others have suggested and replace it with List
your List TextReader needs to be List string
Change your Console.WriteLine(messages) to Console.WriteLine(line)
make sure you are including the Generics namespace in your usings (for List<>).
make sure you return the correct type of List string. This is the one that is causing the compiler error you are seeing.
public List<string> ReadMessages(string nMessages)
{
List<string> messages = new List<string>;
using (StreamReader stre = new StreamReader(_FilePath))
{
while ((line = stre.ReadLine()) != null)
{
messages.Add(line);
Console.WriteLine(line);
}
}
return messages;
}

Obtain Assembly attributes from unloaded executable

I am attempting to retrieve the typical AssemblyInfo attributes from an executable file, but not from the currently executing assembly. I wish to 'look into' a program file (.exe) elsewhere on the drive that I have written in C#.NET and check the AssemblyProduct string.
This is fairly easy and straightforward when you're looking for this information from the currently executing assembly. However, apparently not so much when you attempt to pull it from an unloaded assembly.
When I use the following code, it returns "Microsoft® .NET Framework" instead of the Product name that I put in my AssemblyInfo.cs file.
Note: I use the System.Reflection.AssemblyName object to pull the version info e.g:AssemblyName.GetAssemblyName(pathToAssembly) and this works correctly, but I'm unable to pull my assembly's attributes using that class or by any means I've tried thus far. Is there some other special class, or what am I missing or doing incorrectly here?
public static string GetAppProdIDFromPath(string pathToForeignAssembly)
{
var atts = GetForeignAssemblyAttributes(pathToForeignAssembly);
var id = string.Empty;
foreach (var att in atts)
{
if (att.GetType() == typeof(AssemblyProductAttribute))
{
id = ((AssemblyProductAttribute)att).Product;
}
}
return id;
}
private static object[] GetForeignAssemblyAttributes(string pathToAssembly)
{
if(File.Exists(pathToAssembly))
{
try
{
var assm = System.Reflection.Assembly.LoadFrom(pathToAssembly);
return assm.GetType().Assembly.GetCustomAttributes(false);
}
catch(Exception ex)
{
// logger etc
}
}
else
{
throw...
}
return null;
}
As Duncanp mentioned, there is a bug in my code. Posting it for clarity and for anyone down the road who looks for the same solution:
public static string GetAppProdIDFromPath(string pathToForeignAssembly)
{
var atts = GetForeignAssemblyAttributes(pathToForeignAssembly);
var id = string.Empty;
foreach (var att in atts)
{
if (att.GetType() == typeof(AssemblyProductAttribute))
{
id = ((AssemblyProductAttribute)att).Product;
}
}
return id;
}
private static object[] GetForeignAssemblyAttributes(string pathToAssembly)
{
if(File.Exists(pathToAssembly))
{
try
{
var assm = System.Reflection.Assembly.LoadFrom(pathToAssembly);
return assm.GetCustomAttributes(false); // fixed line
}
catch(Exception ex)
{
// logger etc
}
}
else
{
throw...
}
return null;
}

Roslyn - get grouped single line comments

I am writing a program in C# for extracting comments from code. I am using Roslyn compiler to do that. It's great, because I am just visiting the whole abstract syntax tree and fetching SingleLineComment trivia, MultiLineComment trivia and DocumentationComment trivia syntax from the file in solution. But there is a problem because programmers often write comments like that:
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
You can see that these are three single line comments, but I want them them to be fetched from code as one comment. Can I achieve that with Roslyn or maybe there is another way? Because that's frequent situation when programmers are writing multi line commments using single line comments syntax.
My code for extracting comments looks like this:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Collections.Generic;
namespace RoslynPlay
{
public class CommentStore
{
public List<Comment> Comments { get; } = new List<Comment>();
public void AddCommentTrivia(SyntaxTrivia trivia,
LocationStore commentLocationstore, string fileName)
{
if (trivia.Kind() == SyntaxKind.SingleLineCommentTrivia)
{
Comments.Add(new SingleLineComment(trivia.ToString(),
trivia.GetLocation().GetLineSpan().EndLinePosition.Line + 1, commentLocationstore)
{
FileName = fileName,
});
}
else if (trivia.Kind() == SyntaxKind.MultiLineCommentTrivia)
{
Comments.Add(new MultiLineComment(trivia.ToString(),
trivia.GetLocation().GetLineSpan().StartLinePosition.Line + 1,
trivia.GetLocation().GetLineSpan().EndLinePosition.Line + 1, commentLocationstore)
{
FileName = fileName,
});
}
}
public void AddCommentNode(DocumentationCommentTriviaSyntax node,
LocationStore commentLocationstore, string fileName)
{
Comments.Add(new DocComment(node.ToString(),
node.GetLocation().GetLineSpan().StartLinePosition.Line + 1,
node.GetLocation().GetLineSpan().EndLinePosition.Line,
commentLocationstore)
{
FileName = fileName,
});
}
}
}
and in main main file (Program.cs) I am launching comment extraction from code like this:
string fileContent;
SyntaxTree tree;
SyntaxNode root;
CommentsWalker commentWalker;
MethodsAndClassesWalker methodWalker;
string[] files = Directory.GetFiles(projectPath, $"*.cs", SearchOption.AllDirectories);
var commentStore = new CommentStore();
Console.WriteLine("Reading files...");
ProgressBar progressBar = new ProgressBar(files.Length);
foreach (var file in files)
{
fileContent = File.ReadAllText(file);
string filePath = new Regex($#"{projectPath}\\(.*)$").Match(file).Groups[1].ToString();
tree = CSharpSyntaxTree.ParseText(fileContent);
root = tree.GetRoot();
commentWalker = new CommentsWalker(filePath, commentStore);
commentWalker.Visit(root);
progressBar.UpdateAndDisplay();
}
and here is also the comment walker:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace RoslynPlay
{
public class CommentsWalker : CSharpSyntaxWalker
{
private string _fileName;
private CommentStore _commentStore;
public CommentsWalker(string fileName,
CommentStore commentStore)
: base(SyntaxWalkerDepth.StructuredTrivia)
{
_fileName = fileName;
_commentStore = commentStore;
}
public override void VisitTrivia(SyntaxTrivia trivia)
{
if (trivia.Kind() == SyntaxKind.SingleLineCommentTrivia
|| trivia.Kind() == SyntaxKind.MultiLineCommentTrivia)
{
_commentStore.AddCommentTrivia(trivia, _commentLocationStore, _fileName);
}
base.VisitTrivia(trivia);
}
public override void VisitDocumentationCommentTrivia(DocumentationCommentTriviaSyntax node)
{
_commentStore.AddCommentNode(node, _commentLocationStore, _fileName);
base.VisitDocumentationCommentTrivia(node);
}
}
}
And the problem is because trivia.Kind() == SyntaxKind.SingleLineCommentTrivia extracts only single line of comments, but I want to extract single line comments blocks as one comment.

String is null after binary serialization

I'm making a simple game, one of the features is challenges which are going to be opened from a file, here is how one of the challenges look:
[System.Serializable]
public class Challenge10 : ChallegeInterface {
public bool isCompleted = false;
public string description = "test";
public int curCoins;
public override void giveReward (){
GameDataSingleton.gameDataInstance.game_data.addCoinsFromReward (7);
}
//Method to check if everything is completed
public override bool isComplete (){
if (!isCompleted) {
curCoins= GameDataSingleton.gameDataInstance.game_data.getCoinsThisGame ();
if (curCoins >= 1) {
isCompleted = true;
giveReward ();
return true;
}
}
return false;
}
}
The problem is that after I deserielize the file the string value(description) is null. Here is the code where the program is opening the challenges.
public void openChallenges(){
challenges = new ChallegeInterface[game_data.challenges.Length];
for (int i = 0; i < game_data.challenges.Length; i++) {
int curChallenge = game_data.challenges[i];
ChallegeInterface challenge = HddManager.HddManagerInstance.load<ChallegeInterface> ("challenges/challenge"+curChallenge);
Debug.Log (challenge.description);
challenges[i] = challenge;
}
}
Everything else seems fine, except the description. Am i missing something?
Edit:
This is how the program is serializing the object:
public void save<T>(T data, string fileName) where T : class{
if (fileName == "")
Debug.Log ("Empty file path");
FileStream file = null;
try{
if(fileName.IndexOf("/") > 0){
string[] strDirName = fileName.Split(new char[] {'/'});
string dirName = strDirName[0];
if(!Directory.Exists(Path.Combine(Application.persistentDataPath, dirName))){
Directory.CreateDirectory(Path.Combine(Application.persistentDataPath, dirName));
}
}
file = File.Create(Path.Combine(Application.persistentDataPath, fileName));
binFormatter.Serialize(file, data);
Debug.Log ("File saved succesfully" + fileName);
}catch(IOException e){
Debug.Log(e.ToString());
}finally{
if(file != null)
file.Close();
}
}
This is how the object is deserialized:
public T load<T> (string fileName) where T : class{ // T => object type
if (fileName == null) {
Debug.Log ("Empty path to file");
return null;
} else {
FileStream file = null;
try {
//Open the file;
file = File.Open (constructFilePath(fileName), FileMode.Open);
//To be removed
Debug.Log ("File loaded succesfully");
// Deserialize the opened file, cast it to T and return it
return binFormatter.Deserialize (file) as T;
} catch (IOException e) {
//To be removed
Debug.Log (e.ToString ());
// Saves the current object in case the file doesn't exist
// Use Activator to create instance of the object,
save (Activator.CreateInstance<T> (), fileName);
// calls the function again
return load<T> (fileName);
} finally {
//Close the file
if (file != null)
file.Close ();
}
}
}
By inspecting the following lines in the openChallenges() method:
ChallegeInterface challenge = HddManager.HddManagerInstance.load<ChallegeInterface> ("challenges/challenge"+curChallenge);
Debug.Log (challenge.description);
I assume ChallegeInterface is actually a class and not an interface since interfaces can not contain fields and you are accessing challenge.description when challenge is ChallegeInterface which means ChallengeInterface is a class.
In this case you are actually accessing the base class' (ChallegeInterface) field instead of the correct one (Challenge10). and that field is empty.
Important: keep clear and correct coding conventions, never name a class Interface, it's better to avoid naming types with programming terminology instead of indicative naming related to their usage.
P.S.: I've checked the serialization myself and inspected Challenge10's description and it works fine.

Constructor not being called

I cannot figure out why I keep getting a null ref on filename when I'm clearly calling this singleton and it should be calling the Logger() to set the filename variable:
public class Logger
{
private static Logger defaultLogger = null;
readonly string filename;
public static Logger DefaultLogger
{
get
{
// Check for valid instance
if (defaultLogger == null)
defaultLogger = new Logger();
// Return instance
return defaultLogger;
}
}
private Logger()
{
filename = ConfigurationManager.AppSettings["MyLogPath"];
}
public string Filename
{
get { return this.filename; }
}
public void Write(EntryType type, string data)
{
lock (this)
{
using (StreamWriter writer = new StreamWriter(filename, true))
{
//do something
}
}
}
}
Here's how I'm calling this class:
Logger.DefaultLogger.Write(EntryType.Error, e.ToString());
So I get this error during runtime saying that filename is null:
Value cannot be null.
Parameter name: path
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.ArgumentNullException: Value cannot be null.
Parameter name: path
Source Error:
Line 49:
Line 50:
Line 51: public string Filename
Line 52: {
Line 53: get { return this.filename; }
This was our original code (I did not write this), same problem:
public class Logger
{
public static Logger DefaultLogger = new Logger();
string filename;
public Logger()
{
filename = ConfigurationManager.AppSettings["LogPath"];
}
public Logger(string filename)
{
this.filename = filename;
}
public string Filename
{
get { return this.filename; }
}
public void Write(LogEntryType type, string data)
{
lock ()
{
using (StreamWriter writer = new StreamWriter(filename, true))
{
...
}
}
}
Is it because
ConfigurationManager.AppSettings["MyLogPath"];
is null?
Check that your App.Config file is there ( it takes the form of yourexe.exe.config in your bin folder). You App.Config should have the following lines:
<configuration>
<appSettings>
<add key="MyLogPath" value="C:\Simple.txt" />
<appSettings>
</configuration>
Maybe in order to test, you can temporary set the filename to a well-know path ( filename=#"D:\C#\mytext.txt";) and see whether you get the error or not.
If I set the filename explicitly, then I won't have such an error, OTOH,if I set
filename=ConfigurationManager.AppSettings["MyLogPath"];
then I will get a
System.ArgumentNullException : Value
cannot be null. Parameter name: path
at
System.IO.StreamWriter..ctor(String
path, Boolean append, Encoding
encoding, Int32 bufferSize) at
System.IO.StreamWriter..ctor(String
path, Boolean append)
If I stepped using a debugger, I can see that it failed at:
(StreamWriter writer = new StreamWriter(filename, true))
I don't know why your debugger won't hit the constructor. My debugger hit the constructor in both cases.
Without looking into it too much..
private Logger()
{
filename = ConfigurationManager.AppSettings["MyLogPath"];
throw new Exception("filename = "+filename);
}
Does the exception get thrown?
I copy pasted the code and replaced
filename = ConfigurationManager.AppSettings["MyLogPath"];
with
filename = #"test.log";
And it works fine. So your error is in the spelling of "MyLogPath" or in the app.config .
Static initilaizers are executed when the class definition is first accessed, which in your case means they are executed before your private constructor.
You're using a lock, so you expect this to be called in a multithreaded scenario, but you don't synchronize DefaultLogger. Also, are you sure you're setting fileName to something non-null in the first place?
Try assigning your Logger to instance variable in your client code and accessing it's FileName. Perhaps it's not getting assigned correctly.
Also, do you need to have filename as readonly if you're only providing a get property?

Categories