我可以在真实设备上使用基本 Activity 生成 Spec 对象。但是,通过 Robolectric 在 JVM 下使用 Android SDK 时,我无法重现 Spec 对象。我需要在 Robolectric 单元测试中增加什么来复制与真实设备相同的行为?
以下是适用于真实设备的代码。
package net.lapasa.testkeypairgeneratorspec;
import android.os.Bundle;
import android.security.KeyPairGeneratorSpec;
import android.support.v7.app.AppCompatActivity;
import java.math.BigInteger;
import java.util.Calendar;
import javax.security.auth.x500.X500Principal;
public class MainActivity extends AppCompatActivity
{
private static final String TEST_ALIAS = "TEST_ALIAS";
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
KeyPairGeneratorSpec.Builder builder = new KeyPairGeneratorSpec.Builder(this); //mock(KeyPairGeneratorSpec.Builder.class);
KeyPairGeneratorSpec spec = getSpec(builder); // THIS WORK, NOT NULL
}
private KeyPairGeneratorSpec getSpec(KeyPairGeneratorSpec.Builder builder)
{
Calendar notBefore = Calendar.getInstance();
Calendar notAfter = Calendar.getInstance();
notAfter.add(Calendar.YEAR, 1);
String alias = TEST_ALIAS;
builder.setAlias(alias);
builder.setSubject(new X500Principal(String.format("CN=%s, OU=%s", alias, getPackageName())));
builder.setSerialNumber(BigInteger.ONE);
builder.setStartDate(notBefore.getTime());
builder.setEndDate(notAfter.getTime());
KeyPairGeneratorSpec spec = builder.build();
return spec;
}
}
但是,当尝试在 Robolectric 单元测试中复制此行为时,它无法返回 Spec 对象:
package net.lapasa.testkeypairgeneratorspec;
import android.security.KeyPairGeneratorSpec;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.math.BigInteger;
import java.security.Security;
import java.util.Calendar;
import javax.security.auth.x500.X500Principal;
import static org.junit.Assert.assertNotNull;
@RunWith(RobolectricTestRunner.class)
public class SpecGenerationTest
{
public SpecGenerationTest()
{
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
}
private KeyPairGeneratorSpec getSpec(KeyPairGeneratorSpec.Builder builder)
{
Calendar notBefore = Calendar.getInstance();
Calendar notAfter = Calendar.getInstance();
notAfter.add(Calendar.YEAR, 1);
String alias = "Test Alias";
builder.setAlias(alias);
builder.setSubject(new X500Principal(String.format("CN=%s, OU=%s", alias, RuntimeEnvironment.application.getPackageName())));
builder.setSerialNumber(BigInteger.ONE);
builder.setStartDate(notBefore.getTime());
builder.setEndDate(notAfter.getTime());
KeyPairGeneratorSpec spec = builder.build();
return spec;
}
@Test
public void testGenerateSpec() throws Exception
{
KeyPairGeneratorSpec.Builder builder = new KeyPairGeneratorSpec.Builder(RuntimeEnvironment.application);
KeyPairGeneratorSpec spec = getSpec(builder);
assertNotNull(spec); // THIS DOESN'T WORK, WHY IS IT NULL?
}
}
更新:这是我尝试创建自定义阴影的尝试,但它仍然返回 null。我不确定参数是否实际设置在 -REAL OBJECT-
package shadow.net.lapasa.testkeypairgeneratorspec;
import android.content.Context;
import android.security.KeyPairGeneratorSpec;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.RealObject;
import org.robolectric.internal.Shadow;
import java.math.BigInteger;
import java.util.Date;
import javax.security.auth.x500.X500Principal;
@Implements(KeyPairGeneratorSpec.Builder.class)
public class ShadowKeyPairGeneratorSpecBuilder
{
@RealObject
private KeyPairGeneratorSpec.Builder builder;
private Context context;
private X500Principal subject;
private BigInteger serialNumber;
private Date startDate;
private Date endDate;
private String alias;
public void __constructor__(Context context)
{
this.context = context;
}
/**
* Sets the alias to be used to retrieve the key later from a KeyStore instance using the AndroidKeyStore provider.
*
* @param alias
*/
@Implementation
public void setAlias(String alias)
{
this.alias = alias;
Shadow.directlyOn(builder, KeyPairGeneratorSpec.Builder.class).setAlias(alias);
}
/**
* Sets the subject used for the self-signed certificate of the generated key pair.
*
* @param subject
*/
@Implementation
public void setSubject(X500Principal subject)
{
this.subject = subject;
Shadow.directlyOn(builder, KeyPairGeneratorSpec.Builder.class).setSubject(subject);
}
/**
* Sets the serial number used for the self-signed certificate of the generated key pair.
*
* @param serialNumber
*/
@Implementation
public void setSerialNumber(BigInteger serialNumber)
{
this.serialNumber = serialNumber;
Shadow.directlyOn(builder, KeyPairGeneratorSpec.Builder.class).setSerialNumber(serialNumber);
}
/**
* Sets the start of the validity period for the self-signed certificate of the generated key pair.
*
* @param startDate
*/
@Implementation
public void setStartDate(Date startDate)
{
this.startDate = startDate;
Shadow.directlyOn(builder, KeyPairGeneratorSpec.Builder.class).setStartDate(startDate);
}
/**
* Sets the subject used for the self-signed certificate of the generated key pair.
*
* @param endDate
*/
@Implementation
public void setEndDate(Date endDate)
{
this.endDate = endDate;
Shadow.directlyOn(builder, KeyPairGeneratorSpec.Builder.class).setEndDate(endDate);
}
@Implementation
public KeyPairGeneratorSpec build()
{
// KeyPairGeneratorSpec spec = Shadow.directlyOn(builder, KeyPairGeneratorSpec.Builder.class, "build", null);
KeyPairGeneratorSpec spec = builder.setAlias(alias).setSubject(subject).setSerialNumber(serialNumber).setStartDate(startDate).setEndDate(endDate).build();
// KeyPairGeneratorSpec spec = Shadow.directlyOn(builder, KeyPairGeneratorSpec.Builder.class).build();
return spec; // Cannot instantiate KeyPairGeneratorSpec directly because it's not available publically. It can only be generated through KeyPairGeneratorSpec.Builder generator
}
}