从单元测试中调用getSharedPreferences()时UnsupportedOperationException

从单元测试中调用getSharedPreferences()时UnsupportedOperationException

问题描述:

我已经实现了Content Provider,它从安装在网络上的服务中检索数据。 内容提供商需要从共享偏好所需的参数(用户凭证,应用程序名称等)从单元测试中调用getSharedPreferences()时UnsupportedOperationException

SharedPreferences settings = getContext().getSharedPreferences(NET_PREFS, 0); 

我使用的单元测试该内容提供商:


public class ResearchProviderTest extends ProviderTestCase2<MyProvider>{ 

    public ResearchProviderTest() {  
     super(MyProvider.class, MyProvider.CONTENT_AUTHORITY); 
    } 

    public void testInsert() { 

     ContentValues values = new ContentValues();   
     values.put(QueryFields.FIELD_QUERY_TEXT, "query"); 

     Uri newUri = getMockContentResolver().insert(MyProvider.CONTENT_URI, values); 

     Cursor readMessagesCursor = getMockContentResolver().query(
       newUri , 
         new String[] { FIELD_SR_TITLE }, 
         null, null, null); 
     assertTrue("The cursor should contain some entries", readMessagesCursor.moveToFirst()); 
    } 
} 

在单元测试的执行。抛出异常:


java.lang.UnsupportedOperationException 
    at android.test.mock.MockContext.getSharedPreferences(MockContext.java:127) 
    at android.content.ContextWrapper.getSharedPreferences(ContextWrapper.java:146) 
    at android.content.ContextWrapper.getSharedPreferences(ContextWrapper.java:146) 
    at com.company.mobile.android.net.RemoteExecutor.retrieveCredentials(RemoteExecutor.java:171) 
    at com.company.mobile.android.net.service.Adapter.executeSearch(Adapter.java:33) 
    at com.company.mobile.android.provider.MyProvider.insert(MyProvider.java:167) 
    at android.content.ContentProvider$Transport.insert(ContentProvider.java:174) 
    ... 

有没有人知道如何将共享首选项传递给MockContext?如果在测试Content Provider期间需要Context,我应该使用其他方法吗?

你必须注入,使用AndroidTestCase.setContext()延伸MockContext和覆盖至少getPackageName()getSharedPreferences()一个Context

不幸的是,不可能将MockContext注入到ProviderTestCase2,因为setContext()未得到遵守。然后,解决方案是覆盖setUp()不调用super方法。

package com.example.aatg.ps.test; 

import android.test.ProviderTestCase2; 

import com.example.aatg.ps.ProviderSample; 

public class ProviderSampleTests extends ProviderTestCase2<ProviderSample> { 

    private ResolverRenamingMockContext mProviderContext; 
    private Class<ProviderSample> mProviderClass; 
    private ProviderSample mProvider; 
    private String mProviderAuthority; 

    public ProviderSampleTests() { 
     super(ProviderSample.class, ProviderSample.AUTHORITY); 
     setName("ProviderSampleTests"); 
     mProviderClass = ProviderSample.class; 
     mProviderAuthority = ProviderSample.AUTHORITY; 
    } 

    @Override 
    protected void setUp() throws Exception { 
     // We should not invoke super.setUp() here to avoid the UnsupportedOperationException. 
     //super.setUp(); 

     // This is needed to support getSharedPreferences() in ProviderSample.onCreate() 
     mProviderContext = new ResolverRenamingMockContext(getContext()); 
     mProvider = mProviderClass.newInstance(); 
     assertNotNull(mProvider); 
     mProvider.attachInfo(mProviderContext, null); 
     mProviderContext.addProvider(mProviderAuthority, mProvider); 
    } 


    @Override 
    public ProviderSample getProvider() { 
     return mProvider; 
    } 

    protected void tearDown() throws Exception { 
     super.tearDown(); 
    } 

    public void testProviderSampleCreation() { 
     ProviderSample provider = getProvider(); 
     assertNotNull(provider); 
    } 
} 

这是ResolverRenamingMockContext

package com.example.aatg.ps.test; 

import java.io.File; 

import android.content.ContentProvider; 
import android.content.Context; 
import android.content.SharedPreferences; 
import android.content.res.Resources; 
import android.test.IsolatedContext; 
import android.test.mock.MockContentResolver; 
import android.test.mock.MockContext; 

/** 
* @author diego 
* 
*/ 
public class ResolverRenamingMockContext extends IsolatedContext { 

    /** 
    * The renaming prefix. 
    */ 
    private static final String PREFIX = "test."; 


    /** 
    * The resolver. 
    */ 
    private static final MockContentResolver RESOLVER = new MockContentResolver(); 

    /** 
    * Constructor. 
    * @param context 
    */ 
    public ResolverRenamingMockContext(Context context) { 
     super(RESOLVER, new DelegatedMockContext(context)); 
    } 

    public MockContentResolver getResolver() { 
     return RESOLVER; 
    } 

    public void addProvider(String name, ContentProvider provider) { 
     RESOLVER.addProvider(name, provider); 
    } 

    /** 
    * The DelegatedMockContext. 
    * 
    */ 
    private static class DelegatedMockContext extends MockContext { 

     private Context mDelegatedContext; 

     public DelegatedMockContext(Context context) { 
      mDelegatedContext = context; 
     } 

     @Override 
     public SharedPreferences getSharedPreferences(String name, int mode) { 
      return mDelegatedContext.getSharedPreferences(PREFIX + name, mode); 
     } 
    } 

} 
+0

注射上下文是一个主要问题。我没有找到注入Context的方法 – Glaxalg 2011-03-15 22:40:09

只是延长AndroidTestCase代替ProviderTestCase2和使用getContext()

你可以重构你的内容提供者共享偏好的所有使用推入一个MyHelper类。

默认MyHelper实现可以使用共享首选项,就像您现在正在使用它并通过getters返回所需的值。

在测试过程中,您可以注入一个特殊的MyHelper实现,一个不使用共享首选项。也许它被称为TestingHelper它只是返回测试定义的常量。

例如,在ResearchProviderTest

@Override 
protected void setUp() throws Exception { 
    // set up normally 
    super.setUp(); 

    // inject a special helper for testing 
    MyProvider provider = (MyProvider) getProvider(); 
    provider.setHelper(new TestingHelper()); 
}