Com a introdução de sinalizações de lançamento de recursos, há novas políticas de teste que você precisa seguir:
- Seus testes precisam abranger os comportamentos ativado e desativado da sinalização.
- Você deve usar os mecanismos oficiais para definir os valores dos sinalizadores durante os testes.
- Os testes xTS não devem substituir os valores de flag nos testes.
A próxima seção apresenta os mecanismos oficiais que você precisa usar para aderir a essas políticas.
Testar o código sinalizado
Cenário de teste | Mecanismo usado |
---|---|
Teste local quando os valores de sinalização mudam com frequência | Android Debug Bridge, conforme discutido em Mudar o valor de uma flag durante a execução. |
Testes locais quando os valores de flag não mudam com frequência | Arquivo de valores de sinalização, conforme discutido em Definir valores de sinalização de inicialização de recurso |
Testes completos em que os valores de sinalização mudam | FeatureFlagTargetPreparer , conforme discutido em Criar testes completos. |
Teste de unidade em que os valores das sinalizações mudam | SetFlagsRule com @EnableFlags e @DisableFlags , conforme discutido em Criar testes de unidade (Java e Kotlin) ou
Criar testes de unidade (C e C++) |
Teste completo ou de unidade em que os valores de sinalização não podem ser alterados | CheckFlagsRule , conforme discutido em Criar testes de unidade ou completos em que os valores de flag não mudam |
Criar testes completos
O AOSP oferece uma classe chamada FeatureFlagTargetPreparer
, que permite
fazer testes completos em um dispositivo. Essa classe aceita substituições de valor de flag como
entrada, define essas flags na configuração dos dispositivos antes da execução do teste
e restaura as flags após a execução.
É possível aplicar a funcionalidade da classe FeatureFlagTargetPreparer
nos
níveis de módulo de teste e de configuração de teste.
Aplicar FeatureFlagTargetPreparer em uma configuração de módulo de teste
Para aplicar FeatureFlagTargetPreparer
a uma configuração de módulo de teste, inclua
FeatureFlagTargetPreparer
e as substituições de valor de sinalização no arquivo de configuração
do módulo de teste AndroidTest.xml
:
<target_preparer class="com.android.tradefed.targetprep.FeatureFlagTargetPreparer">
<option name="flag-value"
value="permissions/com.android.permission.flags.device_aware_permission_grant=true"/>
<option name="flag-value"
value="virtual_devices/android.companion.virtual.flags.stream_permissions=true"/>
</target_preparer>
Em que:
target.preparer class
é sempre definido comocom.android.tradefed.targetprep.FeatureFlagTargetPreparer
.option
é a substituição da flag comname
sempre definido comoflag-value
evalue
definido comonamespace/aconfigPackage.flagName=true|false
.
Criar módulos de teste parametrizados com base em estados de flag
Para criar módulos de teste parametrizados com base nos estados de indicador:
Inclua
FeatureFlagTargetPreparer
no arquivo de configuração do módulo de testeAndroidTest.xml
:<target_preparer class="com.android.tradefed.targetprep.FeatureFlagTargetPreparer" >
Especifique as opções de valor da flag na seção
test_module_config
de um arquivo de buildAndroid.bp
:android_test { name: "MyTest" ... } test_module_config { name: "MyTestWithMyFlagEnabled", base: "MyTest", ... options: [ {name: "flag-value", value: "telephony/com.android.internal.telephony.flags.oem_enabled_satellite_flag=true"}, ], } test_module_config { name: "MyTestWithMyFlagDisabled", base: "MyTest", ... options: [ {name: "flag-value", value: "telephony/com.android.internal.telephony.flags.carrier_enabled_satellite_flag=true"}, ], }
O campo
options
contém as substituições de flag comname
sempre definido comoflag-value
evalue
definido comonamespace/aconfigPackage.flagName=true|false
.
Criar testes de unidade (Java e Kotlin)
Esta seção descreve a abordagem para substituir os valores da flag aconfig no nível de classe e método (por teste) em testes Java e Kotlin.
Para escrever testes de unidade automatizados em uma base de código grande com um grande número de flags, siga estas etapas:
- Use a classe
SetFlagsRule
com as anotações@EnableFlags
e@DisableFlags
para testar todas as ramificações de código. - Use o método
SetFlagsRule.ClassRule
para evitar bugs comuns de teste. - Use
FlagsParameterization
para testar suas classes em um amplo conjunto de configurações de sinalizadores.
Testar todas as ramificações de código
Para projetos que usam a classe estática para acessar flags, a
classe auxiliar SetFlagsRule
é fornecida para substituir os valores das sinalizações. O snippet de código a seguir mostra como incluir o SetFlagsRule
e ativar várias flags de
uma só vez:
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import com.example.android.aconfig.demo.flags.Flags;
...
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Test
@EnableFlags({Flags.FLAG_FLAG_FOO, Flags.FLAG_FLAG_BAR})
public void test_flag_foo_and_flag_bar_turned_on() {
...
}
Em que:
@Rule
é uma anotação usada para adicionar a dependência flag-JUnit da classeSetFlagsRule
.SetFlagsRule
é uma classe auxiliar fornecida para substituir valores de sinalização. Para informações sobre comoSetFlagsRule
determina valores padrão, consulte Valores padrão do dispositivo.@EnableFlags
é uma anotação que aceita um número arbitrário de nomes de sinalizações. Ao desativar as flags, use@DisableFlags
. É possível aplicar essas anotações a um método ou uma classe.
Defina valores de sinalização para todo o processo de teste, começando com o
SetFlagsRule
, que é anterior a qualquer método de configuração com anotação @Before
no teste. Os valores de sinalização retornam ao estado anterior quando a
SetFlagsRule
é concluída, ou seja, depois de todos os métodos de configuração com anotação @After
.
Verifique se as flags estão definidas corretamente
Como mencionado anteriormente, SetFlagsRule
é usado com a anotação @Rule
do JUnit, o que significa que o SetFlagsRule
não pode garantir que as sinalizações sejam definidas corretamente durante o construtor da classe de teste ou em qualquer método com anotação @BeforeClass
ou @AfterClass
.
Para garantir que os recursos de teste sejam construídos com o valor de classe correto, use
o método SetFlagsRule.ClassRule
para que eles não sejam
criados até que um método de configuração com anotação @Before
seja usado:
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import com.example.android.aconfig.demo.flags.Flags;
class ExampleTest {
@ClassRule public static final SetFlagsRule.ClassRule mClassRule = new SetFlagsRule.ClassRule();
@Rule public final SetFlagsRule mSetFlagsRule = mClassRule.createSetFlagsRule();
private DemoClass underTest = new DemoClass();
@Test
@EnableFlags(Flags.FLAG_FLAG_FOO)
public void test_flag_foo_turned_on() {
...
}
}
Ao adicionar a regra de classe SetFlagsRule.ClassRule
, o test_flag_foo_turned_on
falhará antes de ser executado quando FLAG_FLAG_FOO
é lido pelo construtor de
DemoClass
.
Se toda a classe precisar de uma flag ativada, mova a anotação @EnableFlags
para
o nível da classe (antes da declaração de classe). Mover a anotação para o
nível da classe permite que SetFlagsRule.ClassRule
garanta que a sinalização seja definida corretamente
durante o construtor da classe de teste ou durante qualquer método com anotação @BeforeClass
ou
@AfterClass
.
Executar testes em várias configurações de sinalização
Como é possível definir valores de sinalização para cada teste, também é possível usar a parametrização para executar testes em várias configurações de sinalização:
...
import com.example.android.aconfig.demo.flags.Flags;
...
@RunWith(ParameterizedAndroidJunit4::class)
class FooBarTest {
@Parameters(name = "{0}")
public static List<FlagsParameterization> getParams() {
return FlagsParameterization.allCombinationsOf(Flags.FLAG_FOO, Flags.FLAG_BAR);
}
@Rule
public SetFlagsRule mSetFlagsRule;
public FooBarTest(FlagsParameterization flags) {
mSetFlagsRule = new SetFlagsRule(flags);
}
@Test public void fooLogic() {...}
@DisableFlags(Flags.FLAG_BAR)
@Test public void legacyBarLogic() {...}
@EnableFlags(Flags.FLAG_BAR)
@Test public void newBarLogic() {...}
}
Com SetFlagsRule
, mas sem a parametrização, essa classe executa
três
testes (fooLogic
, legacyBarLogic
e newBarLogic
). O método fooLogic
é executado com quaisquer valores de FLAG_FOO
e FLAG_BAR
definidos no
dispositivo.
Quando a parametrização é adicionada, o método FlagsParameterization.allCombinationsOf
cria todas as combinações possíveis das sinalizações FLAG_FOO
e FLAG_BAR
:
FLAG_FOO
étrue
eFLAG_BAR
étrue
FLAG_FOO
étrue
eFLAG_BAR
éfalse
FLAG_FOO
éfalse
eFLAG_BAR
étrue
FLAG_FOO
é falso eFLAG_BAR
éfalse
Em vez de mudar diretamente os valores das flags, as anotações @DisableFlags
e
@EnableFlags
modificam os valores com base nas condições do parâmetro. Por
exemplo, legacyBarLogic
é executado somente quando FLAG_BAR
está desativado, o que ocorre em
duas das quatro combinações de sinalizações. O legacyBarLogic
é ignorado para as outras
duas combinações.
Há dois métodos para criar as parametrizações das flags:
FlagsParameterization.allCombinationsOf(String...)
executa 2^n execuções de cada teste. Por exemplo, uma flag executa testes 2x ou quatro flags executam 16x testes.FlagsParameterization.progressionOf(String...)
executa n+1 execuções de cada teste. Por exemplo, uma sinalização executa dois testes e quatro outras executam cinco vezes.
Criar testes de unidade (C e C++)
O AOSP inclui macros de valor de flag para testes C e C++ escritos no framework GoogleTest.
Na origem de teste, inclua as definições de macro e as bibliotecas geradas pelo aconfig:
#include <flag_macros.h> #include "android_cts_flags.h"
Na origem do teste, em vez de usar as macros
TEST
eTESTF
nos casos de teste, useTEST_WITH_FLAGS
eTEST_F_WITH_FLAGS
:#define TEST_NS android::cts::flags::tests ... TEST_F_WITH_FLAGS( TestFWithFlagsTest, requies_disabled_flag_enabled_skip, REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(TEST_NS, readwrite_enabled_flag)) ) { TestFail(); } ... TEST_F_WITH_FLAGS( TestFWithFlagsTest, multi_flags_for_same_state_skip, REQUIRES_FLAGS_ENABLED( ACONFIG_FLAG(TEST_NS, readwrite_enabled_flag), LEGACY_FLAG(aconfig_flags.cts, TEST_NS, readwrite_disabled_flag) ) ) { TestFail(); } ... TEST_WITH_FLAGS( TestWithFlagsTest, requies_disabled_flag_enabled_skip, REQUIRES_FLAGS_DISABLED( LEGACY_FLAG(aconfig_flags.cts, TEST_NS, readwrite_enabled_flag)) ) { FAIL(); } ... TEST_WITH_FLAGS( TestWithFlagsTest, requies_enabled_flag_enabled_executed, REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_NS, readwrite_enabled_flag)) ) { TestWithFlagsTestHelper::executed_tests.insert( "requies_enabled_flag_enabled_executed"); }
Em que:
- As macros
TEST_WITH_FLAGS
eTEST_F_WITH_FLAGS
são usadas em vez das macrosTEST
eTEST_F
. REQUIRES_FLAGS_ENABLED
define um conjunto de flags de lançamento de recursos que precisam atender à condição ativada. É possível gravar essas sinalizações nas macrosACONFIG_FLAG
ouLEGACY_FLAG
.REQUIRES_FLAGS_DISABLED
define um conjunto de sinalizações de recursos que precisam atender à condição desativada. É possível gravar essas flags em macrosACONFIG_FLAG
ouLEGACY_FLAG
.ACONFIG_FLAG (TEST_NS, readwrite_enabled_flag)
é uma macro usada para sinalizações definidas em arquivos aconfig. Essa macro aceita um namespace (TEST_NS
) e um nome de flag (readwrite_enabled_flag
).LEGACY_FLAG(aconfig_flags.cts, TEST_NS, readwrite_disabled_flag)
é uma macro usada para sinalizações definidas na configuração do dispositivo por padrão.
- As macros
No arquivo de build
Android.bp
, adicione as bibliotecas geradas pelo aconfig e as bibliotecas de macros relevantes como uma dependência de teste:cc_test { name: "FlagMacrosTests", srcs: ["src/FlagMacrosTests.cpp"], static_libs: [ "libgtest", "libflagtest", "my_aconfig_lib", ], shared_libs: [ "libbase", "server_configurable_flags", ], test_suites: ["general-tests"], ... }
Execute os testes localmente com este comando:
atest FlagMacrosTests
Se a flag
my_namespace.android.myflag.tests.my_flag
estiver desativada, o resultado do teste será:[1/2] MyTest#test1: IGNORED (0ms) [2/2] MyTestF#test2: PASSED (0ms)
Se a sinalização
my_namespace.android.myflag.tests.my_flag
estiver ativada, o resultado do teste será:[1/2] MyTest#test1: PASSED (0ms) [2/2] MyTestF#test2: IGNORED (0ms)
Criar testes completos ou de unidade em que os valores das sinalizações não mudam
Para casos de teste em que não é possível substituir flags e só é possível filtrar testes
se eles forem baseados no estado atual da flag, use a regra CheckFlagsRule
com
anotações RequiresFlagsEnabled
e RequiresFlagsDisabled
.
As etapas a seguir mostram como criar e executar um teste completo ou de unidade em que os valores de flag não podem ser substituídos:
No código de teste, use
CheckFlagsRule
para aplicar a filtragem de teste. Além disso, use as anotações JavaRequiresFlagsEnabled
eRequiredFlagsDisabled
para especificar os requisitos de sinalização do teste.O teste no dispositivo usa a classe
DeviceFlagsValueProvider
:@RunWith(JUnit4.class) public final class FlagAnnotationTest { @Rule public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); @Test @RequiresFlagsEnabled(Flags.FLAG_FLAG_NAME_1) public void test1() {} @Test @RequiresFlagsDisabled(Flags.FLAG_FLAG_NAME_1) public void test2() {} }
O teste do host usa a classe
HostFlagsValueProvider
:@RunWith(DeviceJUnit4ClassRunner.class) public final class FlagAnnotationTest extends BaseHostJUnit4Test { @Rule public final CheckFlagsRule mCheckFlagsRule = HostFlagsValueProvider.createCheckFlagsRule(this::getDevice); @Test @RequiresFlagsEnabled(Flags.FLAG_FLAG_NAME_1) public void test1() {} @Test @RequiresFlagsDisabled(Flags.FLAG_FLAG_NAME_1) public void test2() {} }
Adicione
jflag-unit
e bibliotecas geradas pelo aconfig à seçãostatic_libs
do arquivo de build para o teste:android_test { name: "FlagAnnotationTests", srcs: ["*.java"], static_libs: [ "androidx.test.rules", "my_aconfig_lib", "flag-junit", "platform-test-annotations", ], test_suites: ["general-tests"], }
Use o seguinte comando para executar o teste localmente:
atest FlagAnnotationTests
Se a flag
Flags.FLAG_FLAG_NAME_1
estiver desativada, o resultado do teste será:[1/2] com.cts.flags.FlagAnnotationTest#test1: ASSUMPTION_FAILED (10ms) [2/2] com.cts.flags.FlagAnnotationTest#test2: PASSED (2ms)
Caso contrário, o resultado será:
[1/2] com.cts.flags.FlagAnnotationTest#test1: PASSED (2ms) [2/2] com.cts.flags.FlagAnnotationTest#test2: ASSUMPTION_FAILED (10ms)
Valores padrão do dispositivo
O SetFlagsRule
inicializado usa valores de sinalização do dispositivo. Se o
valor da flag no dispositivo não for substituído, como com o adb, o valor padrão
será o mesmo
que a configuração de lançamento do build. Se o valor no dispositivo tiver sido
substituído, o SetFlagsRule
vai usar o valor de substituição como
padrão.
Se o mesmo teste for executado em diferentes configurações de lançamento, o
valor das flags não definidas explicitamente com SetFlagsRule
poderá variar.
Depois de cada teste, SetFlagsRule
restaura a instância FeatureFlags
em Flags
para o FeatureFlagsImpl
original, para que ela não tenha efeitos colaterais em
outros métodos e classes de teste.