tafuji's blog

C#, Xamarin, Azure DevOps を中心に書いています。

Xamarin.Android の Unit テストをコマンドラインから実行する

Qiita より転載

はじめに

手順

Android の Instrumentation を利用します

1. TestInstrumentation クラスの実装

Xamarin.Android の Unit Test では、Instrumentation 用に TestSuiteInstrumentation クラスが提供されおり、これを継承したクラスをテストプロジェクトに追加するだけです。

using System;
using Android.App;
using Android.OS;
using Android.Runtime;
using Xamarin.Android.NUnitLite;

[Instrumentation(Name = "uitestapp1.uitestapp1.TestInstrumentation")]
public class TestInstrumentation : TestSuiteInstrumentation
{
    public TestInstrumentation(IntPtr handle, JniHandleOwnership transfer) : base (handle, transfer)
    {
    }

    protected override void AddTests()
    {
        AddTest(Assembly.GetExecutingAssembly());
    }
}

2. テストの実行

  • あらかじめ単体テスト用のアプリケーションを端末にインストールしておきます
  • adb コマンドでテストを実行します
    • 以下の例の "UnitTestApp1.UnitTestApp1" の部分は、自分が作成したテストプロジェクトのパッケージ名に合わせます
    • "uitestapp1.uitestapp1.TestInstrumentation" の部分は、TestSuiteInstrumentation を継承したクラスに付与した Instrumentation 属性の Name に指定した値に合わせてください
adb shell am instrument -w UnitTestApp1.UnitTestApp1/uitestapp1.uitestapp1.TestInstrumentation
  • 結果ファイルの取得
    • テストを実行するとテスト結果が表示されます
    • テスト結果が出力された xml ファイルのパスも出力されます

テスト結果

  • adb pull でテスト結果の XML ファイルを取得します。
adb pull /data/user/0/UnitTestApp1.UnitTestApp1/files/.__override__/TestResults.xml ./TestResults.xml

参考:テスト結果の XML ファイル

テスト結果のファイルは、以下のような XML ファイルになります。(nunit2 の形式)

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!--This file represents the results of running a test suite-->
<test-results name="" total="5" errors="0" failures="1" not-run="1" inconclusive="1" ignored="1" skipped="0" invalid="0" date="2017-06-14" time="02:05:01">
  <environment nunit-version="1.0.0.0" clr-version="4.0.50524.0" os-version="Unix 3.10.0.0" platform="Unix" cwd="/" machine-name="localhost" user="somebody" user-domain="localhost" />
  <culture-info current-culture="ja-JP" current-uiculture="ja-JP" />
  <test-suite type="TestSuite" name="" executed="True" result="Failure" success="False" time="0.049" asserts="2">
    <failure>
      <message><![CDATA[One or more child tests had errors]]></message>
      <stack-trace />
    </failure>
    <results>
      <test-suite type="Assembly" name="/storage/emulated/0/Android/data/UnitTestApp1.UnitTestApp1/files/.__override__/UnitTestApp1.dll" executed="True" result="Failure" success="False" time="0.046" asserts="2">
        <properties>
          <property name="_PID" value="1200" />
          <property name="_APPDOMAIN" value="RootDomain" />
        </properties>
        <failure>
          <message><![CDATA[One or more child tests had errors]]></message>
          <stack-trace />
        </failure>
        <results>
          <test-suite type="TestFixture" name="TestsSample" executed="True" result="Failure" success="False" time="0.035" asserts="2">
            <failure>
              <message><![CDATA[One or more child tests had errors]]></message>
              <stack-trace />
            </failure>
            <results>
              <test-case name="Fail" executed="True" result="Failure" success="False" time="0.019" asserts="1">
                <failure>
                  <message><![CDATA[  Expected: False
  But was:  True
]]></message>
                  <stack-trace><![CDATA[  at UnitTestApp1.TestsSample.Fail () [0x00001] in <3474bbfc30d04efc9adb490971b61d70>:0 
  at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&)
  at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00032] in <3fd174ff54b146228c505f23cf75ce71>:0 
]]></stack-trace>
                </failure>
              </test-case>
              <test-case name="Ignore" executed="False" result="Ignored">
                <properties>
                  <property name="_SKIPREASON" value="another time" />
                </properties>
                <reason>
                  <message><![CDATA[another time]]></message>
                </reason>
              </test-case>
              <test-case name="Inconclusive" executed="True" result="Inconclusive" success="False" time="0.001" asserts="0" />
              <test-case name="NewTest" executed="True" result="Success" success="True" time="0" asserts="0" />
              <test-case name="Pass" executed="True" result="Success" success="True" time="0.002" asserts="1" />
            </results>
          </test-suite>
        </results>
      </test-suite>
    </results>
  </test-suite>
</test-results>