Unity3d JsonUtility.FromJson() read from TextAsset works in app, fails in Test Runner - unity3d

The following code works fine in my application (Unity 2019.3.0f6). It reads from Assets/Resources/lesson-text.json and writes the expected logs to the console:
// file to read lessons from
public TextAsset jsonFile;
internal JsonLessonList LoadLessonFromFile()
{
JsonLessonList testLessonList = JsonUtility.FromJson<JsonLessonList>(jsonFile.text);
foreach (JsonLesson lesson in testLessonList.jsonLessonList)
{
Debug.Log("Found lesson: " + lesson.Name);
}
return testLessonList;
}
I'm wanting to read the same file when using Unity's Test Runner:
[UnityTest]
public IEnumerator TestFileParsesOkTest()
{
JsonLessonList testLessonList = jsonReader.LoadLessonFromFile();
Assert.IsNotNull(testLessonList);
yield return null;
}
but I keep getting this exception:
TestFileParsesOkTest (0.019s)
Unhandled log message: '[Exception] NullReferenceException: Object reference not set to an instance of an object'. Use UnityEngine.TestTools.LogAssert.Expect
JsonReader.LoadLessonFromFile () (at Assets/Scripts/JsonReader.cs:68)
JsonReader.Start () (at Assets/Scripts/JsonReader.cs:37)
NullReferenceException: Object reference not set to an instance of an object
I know the file format is ok because it works from the app. I think the problem is that "TextAsset jsonFile" that is set through the unity editor is not being seen by the Test Runner. How do I make this work?
[Test]
public void JsonFileResourceTest()
{
Assert.IsNotNull(jsonReader.jsonFile);
}
results in:
JsonFileResourceTest (0.020s)
Expected: not null
But was: null
(The test driven development tag is because I got the very simplest read of a file with one field working, and now I want to back up and write a unit test for it and then write tests before coding going forward.)

I figured it out:
[SetUp]
public void Setup()
{
jsonReader = new GameObject().AddComponent<JsonReader>();
jsonReader.jsonFile = Resources.Load("lesson-test") as TextAsset;
}
// Verify class exists
[Test]
public void JsonReaderClassExists()
{
Assert.IsNotNull(jsonReader);
Assert.IsNotNull(jsonReader.jsonFile);
}

Related

Using NUnit, (how) can I test code that sets Thread.CurrentThread.Name?

Consider this Test
[TestFixture]
class Sample
{
[Test]
public void Test()
{
Thread.CurrentThread.Name = "Foo";
}
}
If I debug this test, it passes without error.
If I run this test, it fails with the following exception
System.InvalidOperationException : This property has already been set and cannot be modified.
In run mode, the test's thread's name is "NonParallelWorker".
In debug mode, the test's thread's name is null
As a constraint, assume the code-under-test is not allowed to change, and attempts to set the thread's name, without checking for null first.
E.g.
public void SampleMethodUnderTest()
{
// It is important that this method gets to set this field.
Thread.CurrentThread.Name = "Important Value";
}
My search through the documentation and other's posts has come up dry...
Question
Is there any way to disable/modify NUnit's thread-naming behavior?
Try adding the RequiresThreadAttribute.
[TestFixture]
class Sample
{
[Test, RequiresThread]
public void Test()
{
Thread.CurrentThread.Name = "Foo";
}
}
I think this will work currently, although the fact that this creates an unnamed thread may be an implementation detail, and not something that will necessarily work reliably going forward, I'm not sure. The alternative of course is to create your own user-controlled thread in the test, and pass any exceptions back to NUnit.

How can I force a build process to fail through a Unity editor script?

I want to force the build process to fail if some validation conditions are not met.
I've tried using an IPreprocessBuildWithReport with no success:
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
public class BuildProcessor : IPreprocessBuildWithReport
{
public int callbackOrder => 0;
public void OnPreprocessBuild(BuildReport report)
{
// Attempt 1
// Does not compile because the 'BuildSummary.result' is read only
report.summary.result = BuildResult.Failed;
// Attempt 2
// Causes a log in the Unity editor, but the build still succeeds
throw new BuildFailedException("Forced fail");
}
}
Is there any way to programmatically force the build process to fail?
I'm using Unity 2018.3.8f1.
As of 2019.2.14f1, the correct way to stop the build is to throw a BuildFailedException.
Other exception types do not interrupt the build.
Derived exception types do not interrupt the build.
Logging errors most certainly do not interrupt the build.
This is how Unity handles exceptions in PostProcessPlayer:
try
{
postprocessor.PostProcess(args, out props);
}
catch (System.Exception e)
{
// Rethrow exceptions during build postprocessing as BuildFailedException, so we don't pretend the build was fine.
throw new UnityEditor.Build.BuildFailedException(e);
}
Just for clarity, this will NOT stop the build.:
// This is not precisely a BuildFailedException. So the build will go on and succeed.
throw new CustomBuildFailedException();
...
public class CustomBuildFailedException: BuildException() {}
You can use OnValidate() which seems to be exactly what you're looking for.
Let's say you want to make sure a reference to a UI Text component is not null before building, in the script that should have the text reference, you add
private void OnValidate()
{
if (text == null)
{
Debug.LogError("Text reference is null!");
}
}
Having Debug.LogError calls during the build process actually cause the build to fail.

Unit testing "object reference not set to an instance " at NUnit

i have a ASP.Net project and Nunitasp framework work for unit testing,i have a object in account.aspx.cs file when i tried to test the object(NugetplatformModel) value i get"object reference not set to an instance" error,
my account page code is given below
public partial class Account : System.Web.UI.Page
{
public NugetPlatformModel NugetPlatformModels;
public string result = string.Empty;
protected void Page_Load(object sender, EventArgs e)
{
if (!WebSecurity.IsAuthenticated)
{
Response.Redirect("/login", true);
}
else
{
result = "success";
NugetPlatformModels = new NugetPlatformModel();
}
}
my test case code is given below
[Test]
public void AccountPage_ValidCredential_AccessModel()
{
Browser.GetPage(domain + "account");
string ExpectedPage = domain + "account";
logon();
Account acccountPage = new Account();
AssertEquals("success", acccountPage.result);
AssertEquals("should have license",true,acccountPage.NugetPlatformModels.IsHavingLicense);
}
How can I access and test that code behind variables? when start the testing the NUgetplatformmodel has been assigned i have checked it by debugging but after that in nunit gui it displays null reference error, i thought there is a problem in accessing variable in testcase..please help me..
It seems your code is not complete. From what I see here your account needs to run Page_Load in order to fill result and NugetPlatformModels. But I do not see how this method is launched in your test. Is it run from the constructor of Account?
It would be helpfull if you put all the code for Account in your post.

MSTest fails when I do run all, but works otherwise

So I have a Testclass using MSTest and every test works great if I run them one and one, however if I select 2 tests, namely can_register and cannot_Register_existing_username then the second fails (cannot_register_existing_username).
I have let my testclass inherit from an abstract class that looks like this:
public abstract class RollbackCapabilities
{
private TransactionScope _transactionScope;
[TestInitialize]
public virtual void TestInitialize()
{
_transactionScope = new TransactionScope(TransactionScopeOption.RequiresNew, new TransactionOptions { Timeout = new TimeSpan(0, 10, 0) });
}
[TestCleanup]
public virtual void TestCleanup()
{
Transaction.Current.Rollback();
_transactionScope.Dispose();
}
}
If I comment this file out then it works (but now the data remains in the test-db which I don't want).
With this file above active the second test fails, the tests look like this
[TestMethod]
public void Can_Register()
{
//Arrange
AccountController ac = ControllerFactory.CreateAccountController();
RegisterModel model = new RegisterModel();
model.UserName = "TestUser";
model.Password= "TestPassword";
model.ConfirmPassword = "TestPassword";
//Act
ActionResult result = ac.Register(model);
//Assert
Assert.IsInstanceOfType(result, typeof(RedirectToRouteResult));
Assert.AreEqual("Home", ((RedirectToRouteResult)result).RouteValues["controller"]);
Assert.AreEqual("Index", ((RedirectToRouteResult)result).RouteValues["action"]);
}
[TestMethod]
public void Cannot_Register_Existing_Username()
{
//Arrange
AccountController ac = ControllerFactory.CreateAccountController();
RegisterModel model = new RegisterModel();
model.UserName = "TestUser";
model.Password = "TestPassword";
model.ConfirmPassword = "TestPassword";
ac.Register(model);
RegisterModel model2 = new RegisterModel();
model2.UserName = "TestUser";
model2.Password = "OtherTestPassword";
model2.ConfirmPassword = "OtherTestPassword";
//Act
ActionResult result = ac.Register(model2);
//Assert
Assert.IsInstanceOfType(result, typeof(ViewResult));
Assert.AreEqual("", ((ViewResult)result).ViewName);
Assert.AreEqual(model2, ((ViewResult)result).ViewData.Model);
}
and finally the error i get is as follows:
Test method
Viducate.UnitTests.UserHandling.RegisterTests.Cannot_Register_Existing_Username
threw exception: System.Data.EntityCommandExecutionException: An
error occurred while executing the command definition. See the inner
exception for details. ---> System.Data.SqlClient.SqlException:
Invalid object name 'dbo.Users'.
Thats my problem, not big but very annoying and as mentioned if I run the tests one and one it works, it also works but leaves data in the db if I comment out my RollbackCapabilities class
Okay so I found out that my error was that I had created the database (but not tables) by hand because create database is not supported in multi-transaction.
however creating an empty database means that EF assumes there is tables already and that is why it failed with dont know what dbo.users are.
So what I did was created the tables as well and now it works. However this means I can never run this on a new development machine without first creating the tables and database. so annoying.
I think I will set up another test class that does not inherit my abstract rollback class and hade that create the tables permanently... should solve the problem as long as that runs first.

Where can I find the console or debug output from code executed in the package manager window?

I'm using EntityFramework code first with migrations. From the package manager console, I'm running "update-database". This executes Configuration.Seed(context) which I have overridden.
protected override void Seed(WebContext context)
{
Console.WriteLine("Console Test");
Debug.WriteLine("Debug Test");
Trace.WriteLine("Trace Test");
}
Where can I find that output?
Better yet, How do I output back to the package manager window?
Thx,
Dan
A quick hack I use to be able to quickly find a value in my Seed method is simply to throw an exception with a value I care about, e.g.
throw new Exception(yourValue);
This errors out the Seed, but my exception/value appears in my package manager console.
Where can I find that output?
Sorry, but the quick answer is basically nowhere.
To be precise at least not in the package manager console.
Debug.WriteLine("Debug Test");
Trace.WriteLine("Trace Test");
You can see the output of the Debug... and Trace... methods if you attach another Visual Studio to debug the Visual Studio instance which is running the update-database command. Then in the debuggin VS you can see the output in the Output Window.
Console.WriteLine("Console Test");
You can see the output of the Console... methods if you run the migrations with the
migrate.exe command line tool which comes with EF:
How do I output back to the package manager window?
I have here also bad news, after a quick "reflectoring": with the current implementation of the EF migrations it's not supported to display custom information during execution of the update-database (or any other command).
Running a SQL print command will write to the Package Manager Console. Here is a helper method that I use:
/// <summary>
/// write a message to the Package Manager Console
/// </summary>
public void Debug(string s, params object[] args)
{
var fullString = string.Format(s, args).Replace("'", "''");
Sql(string.Format("print '{0}'", fullString));
}
My needs were similar to yours so I figured I'd document them here in case they could help someone else out. My goal was to display all of the output from the migrations including all of the sql run as part of the Seed method. As a side effect of this solution, you will also be able to see any Debug.Write message in your code.
First create a DebugMigrationsLogger that will write all migration output to Debug.WriteLine (thanks to http://whiteknight.github.io/2013/01/26/efcodeonlymigrations.html):
public class DebugMigrationsLogger : System.Data.Entity.Migrations.Infrastructure.MigrationsLogger
{
public override void Info(string message)
{
Debug.WriteLine(message);
}
public override void Verbose(string message)
{
Debug.WriteLine(message);
}
public override void Warning(string message)
{
Debug.WriteLine("WARNING: " + message);
}
}
Next make sure you have a subclass of DbMigrationsConfiguration for your DbContext:
public class MyDbMigrationsConfiguration : DbMigrationsConfiguration<MyDbContext>
{
public MyDbMigrationsConfiguration()
{
}
protected override void Seed(MartusDb db)
{
//...
}
}
Next you run your migrations as an on-demand unit test so your test runner can capture the output. My unit test looks something like this:
public void MigrateDb_Test()
{
var config = new MyDbMigrationsConfiguration { AutomaticMigrationDataLossAllowed = true };
var migrator = new DbMigrator(config);
var loggingDecorator = new MigratorLoggingDecorator(migrator, new DebugMigrationsLogger());
loggingDecorator.Update();
}
Lastly, set the Database.Log in your DbContext constructor:
public class MyDbContext : DbContext
{
public MyDbContext()
{
Database.Log = message => Debug.WriteLine(message);
}
}
Now whenever you run the MigrateDb_Test() you will see all the output, it has made debugging migrations so much easier for me!
Dirty workaround extending George's answer.
protected override void Seed(YourContext context)
{
using (var seedout = new StringWriter())
{
// do your work
context.Authors.AddOrUpdate(x => x.Id,
new Author() { Id = 1, Name = "Jane Austen" }
);
// some message
seedout.WriteLine("some message");
// commit your work
context.SaveChanges();
seedout.WriteLine("Seed successfully completed.");
// dummy exception to show message on package manager console
throw new Exception(seedout.ToString());
}
}