Liebling, ich habe die Testpyramide auf den Kopf gestellt
FRONTEND
BACKEND
FRONTEND
BACKEND
TESTS
https://de.wikipedia.org/wiki/ISO/IEC_9126
Was nicht getestet ist,
geht kaputt
Was nicht getestet ist,
wird missachtet
Was nicht getestet ist,
wird missachtet
@Test
public void allKeysAreTranslatedInEnglish() {
List<MessageKey> missingEn = newArrayList();
for (MessageKey key : translationLoader.getTranslation()) {
if (key.getValues().get(Locale.GERMAN).hasTranslation() &&
!key.getValues().get(Locale.ENGLISH).hasTranslation()) {
missingEn.add(key);
}
}
String message = "These keys have no English translation in " + listKeys(missingEn);
message += "\n Please provide an English translation";
assertThat(message, missingEn, hasSize(0));
}
java.lang.AssertionError: These keys have no English translation in messages_en.properties:
Account_Api_CollapseAll
Please provide an English translation
Expected: a collection with size <0>
but: collection size was <1>
Was nicht getestet ist,
wird missachtet
> Task :price-stock-service:ktlintMainSourceSetCheck FAILED
C:\Users\..\ServerJob.kt:15 Unused import
C:\Users\..\ServerJob.kt:73 Unexpected indentation (18) (should be 16)
C:\Users\..\ServerJob.kt:74 Unexpected indentation (17) (should be 16)
C:\Users\..\ServerJob.kt:74 Exceeded max line length (160) (cannot be auto-corrected)> gradle ktlintTests?
private final MoneyParser parserDE = new DefaultMoneyParser();
@Test
public void parseCurrencyDE() throws Exception {
assertThat(parserDE.parseCurrency("20,11"), is(new Money("20.11")));
assertThat(parserDE.parseCurrency("42100,00"), is(new Money("42100.00")));
assertThat(parserDE.parseCurrency("42100"), is(new Money("42100")));
assertThat(parserDE.parseCurrency("42.100"), is(new Money("42100")));
assertThat(parserDE.parseCurrency("42.100,00"), is(new Money("42100.00")));
assertThat(parserDE.parseCurrency("42.100,11"), is(new Money("42100.11")));
assertThat(parserDE.parseCurrency("-42.100,11"), is(new Money("-42100.11")));
}
@Test
@DisplayName("Parse German currency")
public void parseCurrencyDE() throws Exception {
// Prepare
MoneyParser parserDE = new DefaultMoneyParser();
// Execute
Money parsed = parserDE.parseCurrency("20,11");
// Verify
assertThat(parsed, is(new Money("20.11")));
}
@Test
@DisplayName("Parse German currency")
public void parseCurrencyDE() throws Exception {
// Prepare
MoneyParser parserDE = new DefaultMoneyParser();
// Execute
Money parsed = parserDE.parseCurrency("20,11");
// Verify
assertThat(parsed, is(new Money("20.11")));
}Kent Beck
Extreme Programming
Test Driven Development
@Test
public void someTest() {
StringBuilder sb = new StringBuilder();
sb.append("test");
sb.append((char) 65);
sb.append((char) 10);
sb.append((char) 13);
sb.append((char) 127);
for (int i = 0; i < 31; i++) {
sb.append((char) i);
}
assertTrue(MessageUtil.foo(sb.toString()).indexOf(10) > -1);
assertTrue(MessageUtil.foo(sb.toString()).indexOf(13) > -1);
assertEquals(MessageUtil.foo(sb.toString()).indexOf(3), -1);
assertEquals(MessageUtil.foo(sb.toString()).indexOf(15), -1);
assertEquals(MessageUtil.foo(sb.toString()).indexOf(28), -1);
}@Test
public void removeCtrlChars_parameterTest() {
StringBuilder sb = new StringBuilder();
sb.append("test");
sb.append((char) 65);
sb.append((char) 10);
sb.append((char) 13);
sb.append((char) 127);
for (int i = 0; i < 31; i++) {
sb.append((char) i);
}
assertTrue(MessageUtil.removeCtrlChars(sb.toString()).indexOf(10) > -1);
assertTrue(MessageUtil.removeCtrlChars(sb.toString()).indexOf(13) > -1);
assertEquals(MessageUtil.removeCtrlChars(sb.toString()).indexOf(3), -1);
assertEquals(MessageUtil.removeCtrlChars(sb.toString()).indexOf(15), -1);
assertEquals(MessageUtil.removeCtrlChars(sb.toString()).indexOf(28), -1);
}@Test
public void removeCtrlCharsKeepsNewLine() {
String newLine = String.valueOf((char) 10); // new line (/n)
String cleaned = MessageUtil.removeCtrlChars(newLine);
assertTrue(MessageUtil.removeCtrlChars(newLine).contains(newLine));
}
@Test
public void removeCtrlCharsRemovesShiftIn() {
String shiftIn = String.valueOf((char) 15); // Shift In control (unicode: \u000F)
String cleaned = MessageUtil.removeCtrlChars(shiftIn);
assertFalse(cleaned.contains(shiftIn));
}Ein Software-Tester
läuft in eine Bar und
Jasmine Harpley - ministryoftesting.com
schlendert in die Bar...
gallopiert in die Bar...
rennt in die Bar...
spaziert in die Bar...
Jokin Aspiazu - ministryoftesting.com
Ein Tester
läuft in eine
Bar und
bestellt
-1 Bier.
@Test
public void parseEmpty() {
MoneyParser parserDE = new DefaultMoneyParser();
assertThrows(ParseException.class, () -> parserDE.parseCurrency(""));
}@Test
public void parseNull() {
MoneyParser parserDE = new DefaultMoneyParser();
assertThrows(ParseException.class, () -> parserDE.parseCurrency(null));
}@Test
public void parseNull() {
MoneyParser parserDE = new DefaultMoneyParser();
assertThat(parserDE.parseCurrency("4.20"), is(new Money("420")));
}Automatisiert
langsam
schnell
mehr Integration
mehr Isolation
Automatisiert
API Tests
Integrations Tests
Component Tests
Automatisiert
Service wird hochgefahren
Test von Service-Funktionen, bei dem andere Services gemockt sind
User Ähnlichen Case, der durch
mehrere Codestellen
(Klassen, Funktionen) durchführt
Funktionalität einer Service-Methode im Zusammenspiel mit allen anderen Komponenten sicherstellen
Automatisiert
public class MoneyParserTestDie 100 schnellsten aus 128 in Modul
max: 0,8 s
4 Fehlgeschlagene Tests
@Test
fun testOrderService() {
val emailService = Mockito.mock(IEmailService::class.java)
val orderService = OrderService(emailService)
val status = orderService.order(Product("IPad"))
assertEquals(status, OrderStatus.OK)
}mockStatic(SessionUtils.class);
mockStatic(JMX.class);
mockStatic(UI.class);
mockStatic(RequestContexts.class);
mockStatic(Activator.class);
mockStatic(RedgiantPlugin.class);
mockStatic(MetricFactory.class);
mockStatic(RedgiantModelActivator.class);
RedgiantModelActivator mockActivator = mock(RedgiantModelActivator.class);
ApplicationContext mockApplicationContext = mock(ApplicationContext.class);
Environment mockEnvironment = mock(Environment.class);
FrameworkRegistry mockFrameworkRegistry = mock(FrameworkRegistry.class);
DOrderProposalDContainer mockProposalItems = mock(DOrderProposalDContainer.class);
IPartnershipAccountCache mockAccountCache = mock(IPartnershipAccountCache.class);
DCompanySettingsItem mockCompanySettingsItem = mock(DCompanySettingsItem.class);
AbstractProperty mockProperty = mock(AbstractProperty.class);
PushConfiguration mockPushConfiguration = mock(PushConfiguration.class);
AbstractProperty mockTriggerProperty = mock(AbstractProperty.class);
IRequestContext mockRequestContext = mock(IRequestContext.class);
Page mockPage = mock(Page.class);
WebBrowser mockWebBrowser = mock(WebBrowser.class);
Customer mockCustomer = mock(Customer.class);
when(mockCustomer.isShopCustomer()).thenReturn(false);
IAccountCacheService mockAccountCacheService = Mockito.mock(IAccountCacheService.class);
RedgiantPlugin mockRedgiantPlugin = mock(RedgiantPlugin.class);
UserProfile mockProfile = new UserProfile(mock(User.class), mockCustomer);MOCKS
EVERYWHERE MOCKS
CartOptimizationManagerTest
@Test
fun mockPojoTest() {
val product = mock(JProduct::class.java)
`when`(product.name).thenReturn("mocked")
assertEquals(product.name, "mocked")
}@Test
fun mockPojoTest() {
val product = mock(JProduct::class.java)
`when`(product.name).thenReturn("mocked")
assertEquals(product.name, "mocked")
product.name = "new"
assertEquals(product.name, "new")
}fun isWeekday(): Boolean {
return when(LocalDateTime.now().dayOfWeek){
DayOfWeek.SUNDAY,DayOfWeek.SATURDAY -> false
else -> true
}
}@RunWith(PowerMockRunner.class)
@PrepareForTest(LocalDateTime.class)
public class MockTimeTest {
@Test
public void testLocalDateTime() {
mockStatic(LocalDateTime.class);
LocalDateTime now = LocalDateTime.of(2023, 2, 8, 19, 0);
when(LocalDateTime.now()).thenReturn(now);
PowerMockito.stub(PowerMockito.method(LocalDateTime.class, "now")).toReturn(now);
LocalDateTime received = LocalDateTime.now();
assertEquals(now, received);
}
fun isWeekdayDefault(now: LocalDateTime = LocalDateTime.now()): Boolean {
return when(now.dayOfWeek){
DayOfWeek.SUNDAY,DayOfWeek.SATURDAY -> true
else -> false
}
}public void testCompanyService(){
Log log = getLog(ThreadUtils.class);
CompanyService companyService = new CompanyService();
Company testCompany = new Company("test");
companyService.saveCompany(testCompany);
Company loaded = companyService.loadCompanyByName("test");
log.debug("Loaded Company: " + loaded);
assertEquals(testCompany, loaded);
}public void testCompanyService(){
Log log = getLog(ThreadUtils.class);
CompanyService companyService = new CompanyService();
Company testCompany = new Company("test");
companyService.saveCompany(testCompany);
Company loaded = companyService.loadCompanyName("test");
if(log.isDebugEnabled()){
log.debug("Loaded Company: " + loaded);
}
assertEquals(testCompany, loaded);
}@Test
public void testResetPasswordDE() {
EmailDef emailDef = new EmailDefMock(EmailTemplate.Password.reset);
ResetPasswordDTO dto = new ResetPasswordDTO("Sehr geehrter Herr Müller",
"mueller@mycomapany.de",
resetPasswordLink);
EmailConfiguration emailConfig = new EmailConfigurationMock(dto, Locale.GERMANY);
EmailConfiguration email = emailDef.createEmail(emailConfig);
String mail = email.getBody();
Approvals.verifyHtml(mail);
}LinkFactory
ServiceProvider
CompanyService
OrderService
@Test
public void tesRedgiantLinkFactoryNotUsesRedgiantPlugin() {
JavaClasses redgiantLinkFactory = new ClassFileImporter()
.importClasses(LinkFactory.class);
ArchRule rule = ArchRuleDefinition
.theClass(LinkFactory.class)
.should()
.onlyDependOnClassesThat()
.areNotAssignableTo(ServerProvider.class)
.because("It's not allowed to use Services." +
" Instead provide them as parameter.");
rule.check(redgiantLinkFactory);
}LinkFactory
ServiceProvider
You are here
You are here
SpringApp
Unit-Tests
Integrations-Tests
Selenium-Tests
SpringApp
Unit-Tests
Integrations-Tests
Selenium-Tests
MariaDB
ElasticSearch
Redis
Such-Service
Image-Service
Tracking-Service
integration-test
CompanyRepository
CompanyService
CompanyServiceAPI
Integration-Test
Company
CompanyService
UserService
CartService
UserService
UserService
CompanyRepository
CompanyService
CompanyServiceAPI
Integration-Test
Company
CompanyService
UserService
CartService
UserService
UserService
CompanyRepository
CompanyService
CompanyServiceAPI
Integration-Test
Company
CompanyService
UserService
CartService
UserService
UserService
CompanyRepository
CompanyService
CompanyServiceAPI
Integartion-Test
Company
CompanyService
CompanyRepository
CompanyService
CompanyServiceAPI
Integartion-Test
Company
CompanyService
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { StudentJpaConfig.class },
loader = AnnotationConfigContextLoader.class),
@PropertySource("h2-datasource.properties")
@Transactional
public class InMemorySampleDBTest {
@Resource
private StudentRepository studentRepository;
@Test
public void givenStudent_whenSave_thenGetOk() {
Student student = new Student(1, "john");
studentRepository.save(student);
Optional<Student> result = studentRepository.findById(1L);
Student found = result.get();
assertEquals("john", found.getName());
}
}jdbc.driverClassName = org.h2.Driver
jdbc.url = jdbc:h2:mem:myDb
hibernate.dialect = org.hibernate.dialect.H2Dialect
hibernate.hbm2ddl.auto = createh2-datasource.properties
@BeforeTestClass
fun initMariaDB() {
val dbContainer: JdbcDatabaseContainer<*> = MariaDBContainer(
DockerImageName.parse("mariadb").withTag("10.4.12"))
}testImplementation("org.testcontainers:testcontainers-bom:1.17.6")
testImplementation('org.testcontainers:mariadb')@BeforeTestClass
fun initMariaDB() {
val dbContainer: JdbcDatabaseContainer<*> = MariaDBContainer(
DockerImageName.parse("mariadb").withTag("10.4.12"))
.withExposedPorts(3306)
.withUsername("user")
.withPassword("jug")
}testImplementation("org.testcontainers:testcontainers-bom:1.17.6")
testImplementation('org.testcontainers:mariadb')INFO o.t.d.DockerClientProviderStrategy : Found Docker environment with local Npipe socket (npipe:////./pipe/docker_engine)
INFO org.testcontainers.DockerClientFactory : Docker host IP address is localhost
INFO org.testcontainers.DockerClientFactory : Connected to docker:
Server Version: 20.10.17
API Version: 1.41
Operating System: Docker Desktop
Total Memory: 25605 MB
INFO 🐳 [testcontainers/ryuk:0.3.4] : Creating container for image: testcontainers/ryuk:0.3.4
INFO 🐳 [testcontainers/ryuk:0.3.4] : Container testcontainers/ryuk:0.3.4 is starting: 7985c01359f682cf674c2e6406ae70e9854ade81577590bf5a37b7bdbfbbbe44
INFO 🐳 [testcontainers/ryuk:0.3.4] : Container testcontainers/ryuk:0.3.4 started in PT1.6241543S
INFO o.t.utility.RyukResourceReaper : Ryuk started - will monitor and terminate Testcontainers containers on JVM exit
INFO org.testcontainers.DockerClientFactory : Checking the system...
INFO org.testcontainers.DockerClientFactory : ✔︎ Docker server version should be at least 1.6.0
INFO 🐳 [mariadb:10.4.12] : Creating container for image: mariadb:10.4.12
INFO 🐳 [mariadb:10.4.12] : Container mariadb:10.4.12 is starting: 0d038e95a9f48ef7169ce0542626a91dd72fc32f8cc5639e9c59314ca7fd7f13
INFO 🐳 [mariadb:10.4.12] : Waiting for database connection to become available at jdbc:mariadb://localhost:63608/test using query 'SELECT 1'
INFO 🐳 [mariadb:10.4.12] : Container is started (JDBC URL: jdbc:mariadb://localhost:63608/test)
INFO 🐳 [mariadb:10.4.12] : Container mariadb:10.4.12 started in PT23.6436749SCompanyRepository
CompanyService
CompanyServiceAPI
Company
CompanyRepository
CompanyService
CompanyServiceAPI
Integartion-Test
Company
CompanyRepository
CompanyService
CompanyServiceAPI
End-2-End-Test
Company
CompanyRepository
CompanyService
CompanyServiceAPI
Unit-Test
Company
CompanyRepository
CompanyService
CompanyServiceAPI
API-Test
Company
CompanyRepository
CompanyService
CompanyServiceAPI
API-Test
Company
mocked
CompanyRepository
CompanyService
CompanyServiceAPI
Company
Repository-Test
< 10 min
Service A
Service B
Scenario: pathMatches('/storefront/checkout') && methodIs('get')
* def response = read('json/checkoutResult.json')Test Automation
MadeSimple.
Service A
Feature: Check Cart has correct Supplier Name
Scenario: Cart has correct Supplier Name
Given url host + '/storefront/carts/'
When method GET
Then status 200
* def cart = get response $.supplierCarts.[0]
* def supplierName = get cart $.supplierName
Then match expectedSupplierName == supplierName
cart_test.feature
Service A
Service B
PortalPurchasingBoardTestSf_cartHasSupplierName.feature
Scenario: pathMatches('/storefront/checkout') && methodIs('get')
* def response = read('json/checkoutResult.json')endpoint.feature
{
"id":"ALFBQU-211112-461",
"orderNumber": "NH1-211110-697",
"customerNumber": "0001",
"orderLimitExceeded": false,
"showCustomerOrderId": true,
"customerOrderIdRequired": false,
"billingAddress": {
"id": "9e4bfcc9-9a84-48de-b0ed-befb051b6108",
"companyName": "ITscope GmbH",
"addition1": "Team Vertrieb Patrick Auth",
"addition2": "",
"street": "Ludwig-Erhard-Allee 202",
"zipCode": "76131",
"city": "Karlsruhe",
"postBox": "",
"iso3country": "FRA",
"phone": "123456",
"fax": "123456",
"email": "",
"clientNumber": "",
"url": "",
"isPrimaryAddress": false,
"isDefaultBillingAddress": true,
"isDefaultDeliveryAddress": true
}
}
checkoutResult.json
Service A
Service A
Service B
mocked
Service A
Service A
Service B
Service A
Service B
import
Service A
Service B
import
data class Stock(
val amount: Int,
val unlimited: Boolean,
val deliveryDate: LocalDate?,
)Service A
Service B
import
Service A
Service B
import
<dependency>
<groupId>de.itscope.catalog</groupId>
<artifactId>price-stock-api</artifactId>
<version>1.8.3</version>
</dependency>pom.xml
Weniger zu testen
Service A
Service A
Service B
import
<dependency>
<groupId>de.itscope.catalog</groupId>
<artifactId>price-stock-api</artifactId>
<version>1.8.3</version>
</dependency>pom.xml
Kotlin
Typescript
Vue
Backend zu Frontend
win win
You are Here
You are Here
RedGiant
2.562
Unit
335
Integration
801
Selenium
0:27
Unit
0:03
Integration
1:05
Selenium
#107
#108
#109
#110
#111
#112
Wackler
Wackler
Wackler
doTestSetup()
-SideApplicationFrameLayout-GlobalSearchContentView-ContextPanelDetailView
-masterDetailLayout-ProductSmallDetailView-ProductSmallHeaderView
-AddToCartWidget-AddToCartMenuBargetDriver().findElement(By.className("button"));
getDriver().findElement(By.name("In den Warenkorb"));
getDriver().findElement(By.id("..AddToCartMenuBar.."));// Open Product Page
IExtendedProduct product = TestProductService.getDefault();
FrontendProductPage productPage = application.openPage().productPage(product);
// Check Product Page Header
assertThat(productPage.getHead().getTitle(), hasText(product.getName()));
assertThat(productPage.getHead().getEan(), hasText(product.getEan()));Application
ProductPage
Head
Title
resellerApplication.action().createNewCartInPortal("New Cart");
CartRow newCart = portal.getCartTable().getCartByName("New Cart");
newCart.action().addProduct(appleiPadAir);
newCart.action().shareFirstLineItem();
newCart.action().addNegotiationPartner(distributorUser.getUserName());
SearchPage<?> searchPage = openPage().globalSearchNegotiationPage();
SellerResponsePage<?> sellerResponsePage =
searchPage.openFirstSellerResponseFromCompany(getCompany());
sellerResponsePage.getFirstRow().setPrice("1.00");
CartColumnTableRow supplierCard =
newCart.getCartColumn().getCartTable().getFirstSupplierCard();
supplierCard = selectNegotiationPosition(1, newCart, supplierCard);PriceRequestTest
// #1 (Buyer) create new cart and open it
resellerApplication.action().createNewCartInPortal("New Cart");
// #2 (Buyer) add Product and start negotiation with distri Also
CartRow newCart = portal.getCartTable().getCartByName("New Cart");
newCart.action().addProduct(appleiPadAir);
newCart.action().shareFirstLineItem();
newCart.action().addNegotiationPartner(distributorUser.getUserName());
// #3 (distri) set a price
SearchPage<?> searchPage = openPage().globalSearchNegotiationPage();
SellerResponsePage<?> sellerResponsePage =
searchPage.openFirstSellerResponseFromCompany(getCompany());
sellerResponsePage.getFirstRow().setPrice("1.00");
// #4 Select negotiation price
CartColumnTableRow supplierCard =
newCart.getCartColumn().getCartTable().getFirstSupplierCard();
supplierCard = selectNegotiationPosition(1, newCart, supplierCard);PriceRequestTest
doTestSetup()
doTestSetup()
getErrorLog()
doTestSetup()
getErrorLog()
1:05
Selenium
0:24
Selenium
1:05
Selenium
0:24
Selenium
Wähle die richtige Etage
Denke zuerst an die Tests
Regelmäßiges
Learn & Adapt
Passes the tests
Reveals intention
Nehmt euch Zeit
fürs Testen
Was nicht getestet ist,
geht kaputt
feed.tobse.eu
Folien
Feedback