ইউনিট টেস্টের সময় স্প্রিং @ ভ্যালু পপুলেটিং করছে


238

আমি ফর্মগুলি যাচাই করতে আমার প্রোগ্রামে ব্যবহৃত একটি সাধারণ শিমের জন্য একটি ইউনিট টেস্ট লেখার চেষ্টা করছি। শিমটি দিয়ে এ্যানোটেট করা হয় @Componentএবং এর একটি বর্গ ভেরিয়েবল থাকে যা ব্যবহার করে আরম্ভ করা হয়

@Value("${this.property.value}") private String thisProperty;

আমি এই শ্রেণীর ভিতরে বৈধতা পদ্ধতির জন্য ইউনিট পরীক্ষা লিখতে চাই, তবে, সম্ভব হলে আমি সম্পত্তি ফাইলটি ব্যবহার না করে এটি করতে চাই। এর পিছনে আমার যুক্তিটি হ'ল, আমি যে বৈশিষ্ট্যগুলির ফাইল থেকে পরিবর্তন করে যাচ্ছি সেই মানটি পরিবর্তিত হলে, আমি এটি আমার পরীক্ষার কেসে প্রভাবিত না করা চাই। আমার পরীক্ষার কেসটি কোডটি পরীক্ষা করে যা মানটিকে বৈধ করে, মানটি নিজেই নয়।

আমার পরীক্ষার শ্রেণীর ভিতরে জাভা কোডটি জাভা ক্লাসের সূচনা করার জন্য এবং সেই শ্রেণীর অভ্যন্তরে স্প্রিং @ ভ্যালু সম্পত্তিটি স্থাপনের জন্য কি সেই উপায়টি দিয়ে পরীক্ষা করার জন্য এটি ব্যবহার করার উপায় আছে?

আমি এটি কীভাবে কাছে দেখতে পেয়েছি তা দেখতে পেয়েছি তবে এখনও একটি বৈশিষ্ট্য ফাইল ব্যবহার করে। আমি বরং এটি সব জাভা কোড হতে চাই।


অনুরূপ সমস্যার জন্য আমি এখানে একটি সমাধান বর্ণনা করেছি । আশা করি এটা সাহায্য করবে.
দিগন্ত 7

উত্তর:


199

সম্ভব হলে আমি এই পরীক্ষাটি স্প্রিং কনটেক্সট ছাড়াই লেখার চেষ্টা করব। যদি আপনি এই পরীক্ষায় বসন্ত ছাড়াই এই শ্রেণী তৈরি করেন তবে এর ক্ষেত্রগুলিতে আপনার সম্পূর্ণ নিয়ন্ত্রণ রয়েছে।

@valueক্ষেত্রটি সেট করতে আপনি স্প্রিংস ব্যবহার করতে পারেন ReflectionTestUtils- এটিতে setFieldব্যক্তিগত ক্ষেত্র সেট করার একটি পদ্ধতি রয়েছে।

@ জাভাডোক : রিফ্লেকশনস্টেস্টিলস.সেটফিল্ড (java.lang.Object, java.lang.String, java.lang.Object)


2
ঠিক আমি কী করতে চেষ্টা করছিলাম এবং আমার ক্লাসের মধ্যে মান নির্ধারণের জন্য আমি কী খুঁজছিলাম, ধন্যবাদ!
কাইল

2
বা এমনকি স্প্রিং নির্ভরতা ছাড়াই একে একে পরীক্ষার জন্য অ্যাক্সেসযোগ্য করে তুলতে ক্ষেত্রটি ডিফল্ট অ্যাক্সেস (প্যাকেজ সুরক্ষিত) এ পরিবর্তন করে।
আর্ন বার্মিস্টার

22
উদাহরণ:org.springframework.test.util.ReflectionTestUtils.setField(classUnderTest, "field", "value");
অলিভিয়ার

4
আপনি এই ক্ষেত্রগুলি কনস্ট্রাক্টর দ্বারা সেট করে তৈরি করতে এবং তারপরে @Valueকনট্রাক্টর প্যারামিটারে টীকাটি স্থানান্তর করতে চাইতে পারেন । কোডটি ম্যানুয়ালি লেখার সময় এটি পরীক্ষার কোডটিকে আরও সহজ করে তোলে এবং স্প্রিং বুটের কোনও যত্ন নেই।
থরবজর্ন রাভন অ্যান্ডারসন

একটি মাত্র টেস্টকেসের জন্য কেবলমাত্র একটি সম্পত্তি পরিবর্তন করার পক্ষে এটি সেরা উত্তর।
সদস্যরা

193

স্প্রিং ৪.১ থেকে আপনি org.springframework.test.context.TestPropertySourceইউনিট টেস্টের ক্লাস স্তরে টিকা ব্যবহার করে ঠিক কোডে সম্পত্তি মান সেট করতে পারেন । আপনি নির্ভরযোগ্য শিমের দৃষ্টান্তগুলিতে সংযোজন করার জন্য এমনকি এই পদ্ধতির ব্যবহার করতে পারেন

উদাহরণ স্বরূপ

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = FooTest.Config.class)
@TestPropertySource(properties = {
    "some.bar.value=testValue",
})
public class FooTest {

  @Value("${some.bar.value}")
  String bar;

  @Test
  public void testValueSetup() {
    assertEquals("testValue", bar);
  }


  @Configuration
  static class Config {

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertiesResolver() {
        return new PropertySourcesPlaceholderConfigurer();
    }

  }

}

দ্রষ্টব্য:org.springframework.context.support.PropertySourcesPlaceholderConfigurer বসন্তের প্রসঙ্গে উদাহরণ থাকা দরকার

24-08-2017 সম্পাদনা করুন: আপনি যদি স্প্রিংবুট ১.৪.০ ব্যবহার করেন এবং পরে আপনি পরীক্ষা @SpringBootTestএবং @SpringBootConfigurationটীকাগুলি দিয়ে পরীক্ষা শুরু করতে পারেন । আরও তথ্য এখানে

স্প্রিংবুট ক্ষেত্রে আমাদের নিম্নলিখিত কোড আছে

@SpringBootTest
@SpringBootConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@TestPropertySource(properties = {
    "some.bar.value=testValue",
})
public class FooTest {

  @Value("${some.bar.value}")
  String bar;

  @Test
  public void testValueSetup() {
    assertEquals("testValue", bar);
  }

}

3
ধন্যবাদ, অবশেষে কেউ উত্তর দিয়েছিল কীভাবে মূল্যকে ওভাররাইড করা যায় এবং ক্ষেত্র কীভাবে সেট করা যায় না। আমি পোস্টকন্সট্রাক্টের স্ট্রিং ফিল্ড থেকে মানগুলি পেয়েছি এবং তাই স্ট্রিংয়ের মানটি বসন্তের দ্বারা নির্ধারণ করা দরকার, নির্মাণের পরে নয়।
টকিল্যাক্ট

@ ভ্যালু ("aআআএএ") - আপনি কি এটি নিজের ভিতরে ক্লাস কনফিগার ব্যবহার করতে পারবেন?
কল্পনা সনি

কনফিগার স্ট্যাটিক ক্লাস হওয়ায় আমি নিশ্চিত নই। তবে দয়া করে নির্দ্বিধায় পরীক্ষা করুন
Dmytro Boichenko

আমি কীভাবে মকিতো টেস্ট ক্লাসে @ ভ্যালু টীকাটি ব্যবহার করতে পারি?
ব্যবহারকারী 1575601

আমি একটি সেবার জন্য ইন্টিগ্রেশন টেস্ট লিখছি যা সম্পত্তি ফাইল থেকে মান আনার কোনও কোড উল্লেখ করে না তবে আমার অ্যাপ্লিকেশনটিতে কনফিগারেশন ক্লাস রয়েছে যা সম্পত্তি ফাইল থেকে মান আনছে। সুতরাং যখন আমি পরীক্ষা চালাচ্ছি এটি সমাধানহীন স্থানধারীর ত্রুটি দিচ্ছে, "$ {বসন্ত.ডিস.পোর্ট}"
কিংবদন্তি

63

প্রতিচ্ছবি দ্বারা বেসিক ক্ষেত্রগুলি পেতে / সেট করা অপব্যবহার করবেন না

এখানে বেশ কয়েকটি উত্তরে যেমন প্রতিবিম্ব ব্যবহার করা হয় তবে এমন কিছু যা আমরা এড়াতে পারি।
এটি একাধিক ত্রুটিগুলি উপস্থাপন করার সময় এটি একটি ছোট মান নিয়ে আসে:

  • আমরা কেবল রানটাইমে প্রতিবিম্বের সমস্যাগুলি সনাক্ত করি (উদা: ক্ষেত্রগুলি আর বিদ্যমান নয়)
  • আমরা এনকেপসুলেশন চাই তবে একটি অস্বচ্ছ শ্রেণি নয় যা নির্ভরযোগ্যতাগুলি লুকিয়ে রাখে যা দৃশ্যমান হওয়া উচিত এবং শ্রেণিকে আরও অস্বচ্ছ এবং কম পরীক্ষামূলক করে তোলে।
  • এটি খারাপ নকশা উত্সাহ দেয়। আজ আপনি একটি ঘোষণা @Value String field। কাল আপনি এই ক্লাসে তাদের 5বা 10তাদের ঘোষণা করতে পারেন এবং আপনি ক্লাসের নকশাটি হ্রাস করেছেন তা সম্পর্কে সরাসরি অবগত হতেও পারেন না। এই ক্ষেত্রগুলিকে সেট করার জন্য আরও দৃশ্যমান পদ্ধতির (যেমন কনস্ট্রাক্টর), আপনি এই সমস্ত ক্ষেত্রগুলি যুক্ত করার আগে একবার দু'বার ভাবেন এবং আপনি সম্ভবত এগুলিকে অন্য শ্রেণিতে আবদ্ধ করে ব্যবহার করবেন @ConfigurationProperties

আপনার শ্রেণিকে পরীক্ষামূলকভাবে উভয়ই একক এবং একীকরণে পরিণত করুন

আপনার স্প্রিং উপাদান শ্রেণীর জন্য প্লেইন ইউনিট পরীক্ষা (এটি একটি চলমান স্প্রিং ধারক ছাড়াই) এবং ইন্টিগ্রেশন টেস্ট উভয়ই লিখতে সক্ষম হতে আপনাকে এই ক্লাসটি স্প্রিংয়ের সাথে বা ছাড়াই ব্যবহারযোগ্য করতে হবে।
যখন প্রয়োজন হয় না তখন ইউনিট পরীক্ষায় একটি ধারক চালানো একটি খারাপ অভ্যাস যা স্থানীয় বিল্ডগুলি ধীর করে দেয়: আপনি এটি চান না।
আমি এই উত্তরটি যুক্ত করেছি কারণ এখানে কোনও উত্তরই এই পার্থক্যটি দেখায় না এবং তাই তারা নিয়মিতভাবে চলমান ধারকটির উপর নির্ভর করে।

সুতরাং আমি মনে করি যে আপনি এই সম্পত্তি শ্রেণীর অভ্যন্তরীণ হিসাবে সংজ্ঞায়িত করা উচিত:

@Component
public class Foo{   
    @Value("${property.value}") private String property;
    //...
}

একটি কনস্ট্রাক্টর প্যারামিটারে যা স্প্রিং দ্বারা ইনজেকশনের হবে:

@Component
public class Foo{   
    private String property;

    public Foo(@Value("${property.value}") String property){
       this.property = property;
    }

    //...         
}

ইউনিট পরীক্ষার উদাহরণ

আপনি Fooবসন্ত ছাড়াই ইনস্ট্যান্টিয়েট করতে পারেন এবং নির্মাতাকে propertyধন্যবাদ দেওয়ার জন্য কোনও মান ইনজেক্ট করতে পারেন :

public class FooTest{

   Foo foo = new Foo("dummyValue");

   @Test
   public void doThat(){
      ...
   }
}

একীকরণ পরীক্ষার উদাহরণ

আপনি এই সহজ উপায় ধন্যবাদ স্প্রিং বুট সঙ্গে প্রেক্ষাপটে সম্পত্তি ইনজেকশনের পারেন propertiesএর অ্যাট্রিবিউট @SpringBootTest :

@SpringBootTest(properties="property.value=dummyValue")
public class FooTest{

   @Autowired
   Foo foo;

   @Test
   public void doThat(){
       ...
   }    
}

আপনি বিকল্প হিসাবে ব্যবহার করতে পারেন @TestPropertySourceতবে এটি একটি অতিরিক্ত টিকা যুক্ত করে:

@SpringBootTest
@TestPropertySource("property.value=dummyValue")
public class FooTest{ ...}

স্প্রিংয়ের সাথে (স্প্রিং বুট ব্যতীত) কিছুটা জটিল হওয়া উচিত তবে দীর্ঘকাল থেকে আমি স্প্রিং বুট ছাড়াই স্প্রিং ব্যবহার করি না বলে আমি বোকা কিছু বলতে পছন্দ করি না।

পার্শ্ব নোট হিসাবে: আপনার কাছে যদি অনেকগুলি @Valueক্ষেত্র সেট করার থাকে তবে এগুলি দিয়ে বর্ণিত শ্রেণিতে সেগুলি উত্তোলন @ConfigurationPropertiesআরও প্রাসঙ্গিক কারণ আমরা খুব বেশি যুক্তিযুক্ত কোনও নির্মাণকারী চাই না।


দুর্দান্ত উত্তর। শ্রেষ্ঠ এখানে অনুশীলন এছাড়াও কন্সট্রাকটর-সক্রিয়া ক্ষেত্রের জন্য হতে হয় final, অর্থাত্private String final property
kugo2006

1
এটি দুর্দান্ত যে কেউ তা হাইলাইট করেছে। এটি কেবল বসন্তের সাথে কাজ করার জন্য, @ কনটেক্সট কনফিগারেশনে পরীক্ষার অধীনে শ্রেণি যুক্ত করা দরকার।
vimterd

53

আপনি যদি চান তবে আপনি এখনও স্প্রিং কনটেক্সটে আপনার পরীক্ষা চালাতে পারেন এবং প্রয়োজনীয় বৈশিষ্ট্যগুলি বসন্ত কনফিগারেশন শ্রেণির মধ্যে সেট করতে পারেন। যদি আপনি JUnit ব্যবহার করেন তবে স্প্রিংJUnit4ClassRunner ব্যবহার করুন এবং আপনার পরীক্ষাগুলির জন্য ডেডিকেটেড কনফিগারেশন ক্লাসটি সংজ্ঞায়িত করুন:

পরীক্ষার অধীনে ক্লাস:

@Component
public SomeClass {

    @Autowired
    private SomeDependency someDependency;

    @Value("${someProperty}")
    private String someProperty;
}

পরীক্ষার শ্রেণি:

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(classes = SomeClassTestsConfig.class)
public class SomeClassTests {

    @Autowired
    private SomeClass someClass;

    @Autowired
    private SomeDependency someDependency;

    @Before
    public void setup() {
       Mockito.reset(someDependency);

    @Test
    public void someTest() { ... }
}

এবং এই পরীক্ষার জন্য কনফিগারেশন ক্লাস:

@Configuration
public class SomeClassTestsConfig {

    @Bean
    public static PropertySourcesPlaceholderConfigurer properties() throws Exception {
        final PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer();
        Properties properties = new Properties();

        properties.setProperty("someProperty", "testValue");

        pspc.setProperties(properties);
        return pspc;
    }
    @Bean
    public SomeClass getSomeClass() {
        return new SomeClass();
    }

    @Bean
    public SomeDependency getSomeDependency() {
        // Mockito used here for mocking dependency
        return Mockito.mock(SomeDependency.class);
    }
}

এটি বলার পরে, আমি এই পদ্ধতির সুপারিশ করব না, আমি কেবল এখানে রেফারেন্সের জন্য এটি যুক্ত করেছি। আমার মতে আরও ভাল উপায় মকিতো রানার ব্যবহার করা use সেক্ষেত্রে আপনি বসন্তের অভ্যন্তরে পরীক্ষা চালান না, যা আরও স্পষ্ট এবং সহজ।


4
আমি সম্মত হই যে সর্বাধিক যুক্তি মকিতো দিয়ে পরীক্ষা করা উচিত। আমি চাই স্প্রিংয়ের মাধ্যমে পরীক্ষা চালানোর চেয়ে টীকা উপস্থিতি এবং টীকাগুলির সঠিকতার আরও ভাল উপায় ছিল।
Altair7852

29

এটি কাজ করে বলে মনে হচ্ছে, যদিও এটি কিছুটা ভার্বোস (আমি আরও ছোট কিছু চাই):

@BeforeClass
public static void beforeClass() {
    System.setProperty("some.property", "<value>");
}

// Optionally:
@AfterClass
public static void afterClass() {
    System.clearProperty("some.property");
}

2
আমি মনে করি এই উত্তরটি পরিষ্কার হিসাবে এটি স্প্রিং অগ্নিস্টিক, এটি বিভিন্ন পরিস্থিতিতে যেমন ভালো হয় যখন আপনি কাস্টম পরীক্ষার রানার ব্যবহার করতে পারেন এবং কেবল @TestPropertyটীকা যোগ করতে পারবেন না ।
রাস্পাকর্প

এটি কেবল বসন্তের একীকরণ পরীক্ষার পদ্ধতির জন্য কাজ করে। এখানে কিছু উত্তর এবং মন্তব্য একটি মকিতো পদ্ধতির দিকে ঝুঁকছে, যার জন্য এটি অবশ্যই কাজ করে না (যেহেতু মকিতোতে এমন কিছু নেই যা জনবসতি ঘটাবে@Value যা সংশ্লিষ্ট হোক না কেন নির্বিশেষে
স্যান্ডার ভারহাগেন

5

কনফিগারেশনে প্রপার্টিপ্লেসহোল্ডার কনফিগারকে যুক্ত করা আমার পক্ষে কাজ করছে।

@Configuration
@ComponentScan
@EnableJpaRepositories
@EnableTransactionManagement
public class TestConfiguration {
    @Bean
    public DataSource dataSource() {
        EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
        builder.setType(EmbeddedDatabaseType.DERBY);
        return builder.build();
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
        entityManagerFactoryBean.setDataSource(dataSource());
        entityManagerFactoryBean.setPackagesToScan(new String[] { "com.test.model" });
        // Use hibernate
        JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        entityManagerFactoryBean.setJpaVendorAdapter(vendorAdapter);
        entityManagerFactoryBean.setJpaProperties(getHibernateProperties());
        return entityManagerFactoryBean;
    }

    private Properties getHibernateProperties() {
        Properties properties = new Properties();
        properties.put("hibernate.show_sql", "false");
        properties.put("hibernate.dialect", "org.hibernate.dialect.DerbyDialect");
        properties.put("hibernate.hbm2ddl.auto", "update");
        return properties;
    }

    @Bean
    public JpaTransactionManager transactionManager() {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
         transactionManager.setEntityManagerFactory(
              entityManagerFactory().getObject()
         );

         return transactionManager;
    }

    @Bean
    PropertyPlaceholderConfigurer propConfig() {
        PropertyPlaceholderConfigurer placeholderConfigurer = new PropertyPlaceholderConfigurer();
        placeholderConfigurer.setLocation(new ClassPathResource("application_test.properties"));
        return placeholderConfigurer;
    }
}

এবং পরীক্ষা ক্লাসে

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestConfiguration.class)
public class DataServiceTest {

    @Autowired
    private DataService dataService;

    @Autowired
    private DataRepository dataRepository;

    @Value("${Api.url}")
    private String baseUrl;

    @Test
    public void testUpdateData() {
        List<Data> datas = (List<Data>) dataRepository.findAll();
        assertTrue(datas.isEmpty());
        dataService.updateDatas();
        datas = (List<Data>) dataRepository.findAll();
        assertFalse(datas.isEmpty());
    }
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.