locked
Unit Testing with Android Unit Test Project in Xamarin.Forms RRS feed

  • Question

  • User67827 posted

    Hi all,

    I would like to add unit tests for my business logic.

    I created an Android Unit Test Project:

    Then, I add the Android Project reference for test my OperationsService

    But, I can't create an instance of this service

    Someone know why can't I do references between android projects?

    I have no problems for Shared or iOS projects. Even I could do reference to shared project in my Android Unit Test Project.

    Any idea?

    Thanks, Daniel

    Tuesday, December 9, 2014 11:37 AM

All replies

  • User72377 posted

    I am seeing the same issue. I have a iOS, Android, and Shared PCL project. When I create a "iOS Unit Test Project" in Xamarin Studio I can successfully reference classes in my iOS and Shared projects. When I create an "Android Unit Tests Project" in Xamarin studio, I can only reference classes in my Shared project by not my Android project.

    I have added both the Android and Shared projects as references in the Android Unit Test project. But when I build, I get an error saying that I am missing an assembly reference to the Android application project.

    It seems like there might be a dependency issue. When I build the iOS Unit Test project, it first builds the Shared project, then it builds the iOS Application project, and then it builds the iOS Unit Test project. When I build the Android Unit Test project, it first builds the Shared project and then it attempts to build the Android Unit Test project, but it does not build the Android application project.

    The build output looks something like this -

    Building: App_Shared (Release) Build started 1/8/2015 11:57:06 AM. ....... Build succeeded. 0 Warning(s) 0 Error(s) Time Elapsed 00:00:00.0357120 Building: App_DroidTest (Release) ....... TestsSample.cs(4,7): error CS0234: The type or namespace name "Droid" does not exist in the namespace "App". Are you missing an assembly reference?

    Is anyone else seeing this issue? Is there some way to force the Android Unit Test project to first build the Android Application project?

    Thursday, January 8, 2015 8:08 PM
  • User48 posted

    Is there some way to force the Android Unit Test project to first build the Android Application project?

    No. Don't Do That™.

    (Another in a continuing series of "we really need more documentation" and "where the hell is Xamarin.Android 5.0"...)

    Background:

    Resource handling differs between Library and Application projects (helpful example):

    // From Library project:
    partial class Resource {
        partial class String {
            public static int hello = 2130837505;
        }
    }
    
    // From Application project:
    partial class Resource {
        partial class String {
            public const int hello = 2130968578;
        }
    }
    

    The reason for this difference is that Application projects may "override" Library project resources, and in doing so a different resource ID value may be generated by aapt. The Library project needs to use static (not const) members so that the Application startup can "fixup" those values to the correct Application-provided values.

    This is a fundamental difference between Application projects and Library projects, and mirrors their relationship in Java (i.e. we didn't just make this up, Java does similar things with non-final fields for Library-generated R.java files).

    As a result of this difference, adding Application projects as references to Application projects was fraught with peril: it was entirely possible that the "same" resource would get different IDs between the two projects, resulting in e.g. displaying the "wrong" image on the screen. (No public bugzilla bugs about this, but I've fielded several support tickets regarding exactly this.)

    TL;DR: Don't add Application projects to Application projects; things generally won't work as you expect.

    Why does this matter? This matters because Unit Test projects are also Application projects. Consequently -- aside from the CS0234 reported in this thread -- having the Unit Test project reference an Application project was ill advised.

    Xamarin.Android 5.0

    Then came the planning for Xamarin.Android 5.0, which lists as a new feature Android Wear Support.

    You could write Android Wear apps in Xamarin.Android 4.20, but the experience was less than fun, because you had to follow the manual packaging instructions:

    1. Ensure that your Wearable project and Handheld projects had the same version number and package name.
    2. Manually build the Wearable project as a Release build
    3. Manually add (2) into the Resources\raw directory of the Handheld project.
    4. Manually write an XML blob within the Handheld project which refers to (3).
    5. Manually add a <meta-data/> element to the Handheld project's AndroidManifest.xml.

    It's certainly doable, but it's a PITA.

    "So," says (mostly) I, "since adding an Application project to an Application project is ill advised, why don't we take over that behavior!"

    So we did. Both IDEs were updated so that if an Application project is added as a Project reference to another Application project, the added project is assumed to be an Android Wear project. This is done by setting the %(ProjectReference.IsAppExtension) and %(ProjectReference.ReferenceOutputAssembly) metadata:

    <!-- Handheld Project.csproj -->
    <ProjectReference Include="..\WearApp\WearApp.csproj">
      <Project>{INSERT_GUID_HERE}</Project>
      <Name>WearApp</Name>
      <IsAppExtension>True</IsAppExtension>
      <ReferenceOutputAssembly>False</ReferenceOutputAssembly>
    </ProjectReference>
    

    This change in semantics does two things:

    1. It means that the assembly that "WearApp.csproj" generates is not used as an assembly reference in the Handheld project.

      This is why you get the CS0234 errors.

    2. Within Xamarin.Android 5.0, WearApp.csproj will be automatically built as a Release build, added as a Raw resource to the Handheld project, automatically generate the XML blob, and automatically add the <meta-data/> element within the Handheld project's AndroidManifest.xml. The build will also verify that the package and version numbers match.

    Xamarin.Android 5.0?

    The problem here is that the IDEs got Wear project support before Xamarin.Android 5.0 was released, and Xamarin.Android 4.20 has partial support for %(ProjectReference.IsAppExtension), in that it'll remove WearApp assembly outputs from the compile line.

    The result is the worst of both worlds: you can add an Application project as a project reference to another Application project, but it doesn't do anything good (no automagic Wear app building) while making for obscure error conditions like this.

    Doh!

    ...but what about unit testing code?

    Because adding Application projects as project references to Application projects is (still!) not advised (at least until using Wear project support), this was never a good way to unit test your code.

    Thus, what you should instead do is place any code you want to have used by a Unit Test Project be within an Android Library (or PCL) project. This will ensure that the type you're testing can use Android Resources "normally", have things generally work as expected and desired, and your code will be usable by both the Unit Test project and the Application project you really care about.

    Monday, January 19, 2015 9:14 PM
  • User106413 posted

    Hi Jonathan, I just wanted to clarify. Are you suggesting a solution like this:

    MyProj (Solution) - MyProj (PCL) - MyProj.Android (default Xamarin project) - MyProj.Android.Shared (Android Class Library) - MyProj.Android.Test (Android Unit Test App) - MyProj.iOS (default Xamarin project)

    Where MyProj.Android contains almost nothing and MyProj.Android.Shared contains everything (Activities/Bundles et all) so that they can be referenced in the Android unit tests?

    Wednesday, February 11, 2015 3:40 AM
  • User106413 posted

    Hi @JonathanPryor are you able to clarify? We are new to Xamarin Forms and after two weeks we are still struggling to implement any kind of functioning unit test.

    Wednesday, February 18, 2015 11:03 PM
  • User48 posted

    Are you suggesting a solution like this:

    That's a solution, yes. A variation on that is to use a Shared Project between your MyProj.Android and MyProj.Android.Test projects.

    Friday, February 20, 2015 8:27 PM
  • User1661 posted

    Hi,

    A little late on the thread, but I just put out a nuget package 'SpecFlow.Xamarin.Forms' that facilitates unit testing in Xamarin.forms in the console runner (ie for visual studio, team city, etc). It makes it easy to test your ViewModels / Commands/ Internal Navigation.

    In order to keep the test as close to the truth as possible, Xamarin.Forms.Init() is still called by the testing framework, so until we can flag that on/off in the Xamarin.Forms framework, you'll have to catch it.

    You can find the step by step tutorial here.

    Thursday, August 18, 2016 3:20 AM